WordPress8 min readPublished: 16 Jan 2025Updated: 29 Jul 2026

Fix High CPU and Memory Usage on WordPress

Find what is really consuming CPU and memory on a WordPress site: bots, slow queries, heavy plugins or a missing cache, before you pay for a bigger server.

G7Cloud Engineering
Platform team
Share:
Abstract illustration representing fix high cpu and memory usage on wordpress.
  • Confirm the load is genuine before changing anything. A shared server's graph often reflects neighbours rather than your site.
  • Automated traffic is the most common cause. Check the access log by user agent before blaming a plugin.
  • The single highest-value fix is full-page caching, because a cached request uses no PHP and no database at all.
  • Slow queries are usually a missing index or an oversized options table, not a lack of CPU.
  • Upgrade the server last. Doubling capacity on a site being crawled ten thousand times an hour just doubles the bill.

How do I confirm it really is high CPU or memory?

Before changing anything, establish what is actually using the resource, and for how long. A brief peak is normal and harmless. A sustained level for hours is a problem. Those two look identical on a graph with the wrong time range and lead to completely different actions.

Establish the facts
# Load against core count. Load 4 on 4 cores is busy; on 16 it is quiet.
uptime && nproc

# What is consuming CPU right now
ps -eo pcpu,pid,user,comm --sort=-pcpu | head -10

# Total memory across all PHP workers, in MB
ps -ylC php-fpm8.3 --sort:rss | awk 'NR>1 {s+=$8} END {printf "PHP total: %.0f MB\n", s/1024}'

# Database memory
ps -eo rss,comm | grep -E 'mysqld|mariadbd'

# Available memory: this is the column that matters
free -h

# Has the kernel been killing processes?
sudo dmesg -T | grep -i 'killed process' | tail
See checking CPU, memory and disk usage for how to read each of these properly.

On shared hosting the graph may not be about you

A shared server's CPU chart reflects every site on the machine. Your site can be entirely well-behaved and still show constant high usage because of a neighbour. If your own PHP processes are modest while the machine is loaded, that is the answer, and no amount of optimisation on your side will change it.

Is it bots rather than visitors?

This is the first thing to check and the most frequent cause. Crawlers, scrapers and AI training bots can outnumber human visitors many times over, and they do not appear in analytics that rely on JavaScript. The access log is the only place the truth lives.

Read the access log
LOG=/var/log/nginx/access.log

# Top 20 user agents
awk -F'"' '{print $6}' $LOG | sort | uniq -c | sort -rn | head -20

# Top 20 client IPs
awk '{print $1}' $LOG | sort | uniq -c | sort -rn | head -20

# Requests per hour: where is the peak?
awk '{print substr($4,2,14)}' $LOG | sort | uniq -c | tail -24

# Most requested URLs
awk '{print $7}' $LOG | sort | uniq -c | sort -rn | head -20

# Requests hitting the expensive endpoints
grep -cE 'wp-login|xmlrpc|wp-json|\?s=' $LOG
That last line matters: search queries and xmlrpc are uncacheable and expensive, so a small number of them can dominate CPU.
Pattern in the logMeansResponse
One IP with thousands of requestsA scraper or a broken integrationRate limit or block that address
Heavy hits on /?s=Search spam: every one bypasses the cacheRate limit search, or require a longer query
Repeated POSTs to xmlrpc.phpBrute force or pingback abuseDisable xmlrpc.php if nothing uses it
Constant wp-login.php POSTsCredential stuffingRate limit, and use a bot challenge
Many AI crawler user agentsTraining crawlers, often ignoring robots.txtFilter them before they reach PHP

Every row here consumes a PHP worker per request, which is exactly the resource you are short of.

Filtering at the edge costs nothing to serve

Blocking automated traffic inside WordPress still means loading PHP to decide to block it. On G7Cloud, ScaleShield classifies and challenges automated requests before they reach your container, so the CPU is never spent in the first place, and the dashboard shows the human and automated split. See how ScaleShield works.

Which plugin or query is responsible?

Once you have ruled out traffic, find the specific code. Query Monitor gives you a per-request breakdown in the browser, and the MySQL slow query log catches the queries that only misbehave under load.

Enable the slow query log
# In my.cnf, under [mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_queries_not_using_indexes = 1

sudo systemctl restart mariadb

# After some real traffic, summarise it
sudo mysqldumpslow -s t -t 20 /var/log/mysql/slow.log

# What is running right now
mysqladmin processlist
mysql -e "SELECT * FROM information_schema.processlist WHERE time > 5\G"
mysqldumpslow -s t sorts by total time, which surfaces the query that costs the most overall rather than the single slowest run.
Find heavy plugins with WP-CLI
cd /var/www/example.com

# Time the whole page load
wp eval 'timer_start(); do_action("init"); echo timer_stop(3);'

# Autoloaded options: this should be well under 1 MB
wp db query "SELECT ROUND(SUM(LENGTH(option_value))/1024/1024, 2) AS mb
  FROM wp_options WHERE autoload='yes'"

# The worst offenders in that table
wp db query "SELECT option_name, ROUND(LENGTH(option_value)/1024) AS kb
  FROM wp_options WHERE autoload='yes' ORDER BY LENGTH(option_value) DESC LIMIT 20"

# Table sizes
wp db query "SELECT table_name, ROUND(data_length/1024/1024,1) AS data_mb,
  ROUND(index_length/1024/1024,1) AS idx_mb
  FROM information_schema.tables WHERE table_schema=DATABASE()
  ORDER BY data_length DESC LIMIT 15"
The autoloaded options query is the highest-value check here. Every single page load reads that data, on every request.

Autoloaded options are loaded on every request

WordPress reads every option marked autoload on every page load, including admin-ajax and REST calls. Plugins that store logs or cached API responses there can push it past 5 MB, which is 5 MB of database read and PHP unserialisation on every hit. Anything above 1 MB is worth investigating.

  • Install Query Monitor and look at the Queries and Hooks panels on a slow page. It attributes each query to the plugin that issued it, which turns a guess into a name.
  • Deactivate plugins in halves, on a staging copy. Half off, test, then narrow down. Eight plugins takes three rounds rather than eight.
  • Check what runs on admin-ajax.php. Heartbeat, live stock checks and analytics widgets fire repeatedly and are never cached.
  • Look at the scheduler. A stalled queue that suddenly catches up will spike CPU. See WordPress cron and missed tasks.

What actually reduces CPU and memory?

In order of impact per hour of effort. The first item usually beats everything below it combined, because a cached request does not run PHP, does not query the database, and does not consume a worker.

FixTypical impactEffort
Full-page caching for anonymous visitorsVery largeLow
Filtering bots before they reach PHPLarge on a crawled siteLow
OPcache enabled and sized correctlyAround a third of PHP CPULow
Object caching with RedisLarge on logged-in and dynamic pagesMedium
Removing or replacing a heavy pluginVaries, sometimes totalMedium
Adding a missing database indexLarge where it appliesMedium
Trimming the database and options tableModerate, and helps admin speedMedium
A bigger serverBuys headroom, fixes nothingLow, and recurring

Work top to bottom. The last row is where people usually start, which is why the problem returns.

The quick wins
# 1. OPcache. Check it is on and not full.
php -i | grep -E 'opcache.enable|memory_consumption|max_accelerated_files'

# 2. Object caching with Redis
sudo apt install -y redis-server php8.3-redis
wp plugin install redis-cache --activate
wp redis enable
wp redis status

# 3. Prune the options table of expired transients
wp transient delete --expired
wp db query "DELETE FROM wp_options WHERE option_name LIKE '_transient_timeout_%'
  AND option_value < UNIX_TIMESTAMP()"

# 4. Post revisions, which accumulate without limit by default
wp post delete $(wp post list --post_type=revision --format=ids) --force
Back up the database before the last two. They delete rows, and there is no undo. See our backup strategy guide.
Pro Tip

Cap revisions permanently in wp-config.php with define('WP_POST_REVISIONS', 5);. Deleting them once helps; stopping them accumulating again is what keeps the fix in place.

When is a bigger server actually the answer?

When the work is genuine and already efficient. If caching is in place, bots are filtered, queries are indexed and the site is still at capacity during normal trading, you have outgrown the server. That is a good position to be in, and worth paying for. Reaching it before the other fixes just makes the same waste more expensive.

  • Cache hit rate is high and CPU is still saturated. The remaining traffic genuinely needs PHP, which is the definition of having outgrown the machine.
  • Load stays above your core count during ordinary hours, not just during a sale or a campaign.
  • Available memory is consistently low and the out-of-memory killer has been triggered. That is a hard limit, not a tuning problem.
  • A checkout or admin page is slow with no slow queries. Real computation with nowhere left to run.

Isolation removes a whole category of this

On shared hosting your CPU is shared with every other site on the box, so your response times move with their traffic and you cannot tune your way out of it. On G7Cloud each site runs in its own container with its own database and its own PHP pool, so the resources you are measuring are yours. See managed WordPress hosting compared with shared hosting.

For the front-end half of the picture, where server time turns into what visitors actually feel, Core Web Vitals for WordPress covers the measurement that decides your ranking.

Frequently asked questions

What causes high CPU usage on a WordPress site?

Most often automated traffic hitting uncacheable endpoints, followed by slow database queries, a heavy plugin running on every page load, and missing caching. Check the access log by user agent first: bots are the most common cause and the easiest to fix.

How do I find which plugin is using the most resources?

Install Query Monitor and open a slow page: it attributes queries, hooks and timings to the plugin responsible. To confirm, deactivate plugins in halves on a staging copy rather than one at a time, which narrows eight plugins down in three rounds.

Will more RAM fix a slow WordPress site?

Only if you are actually short of memory, which shows up as low available in free -h and out-of-memory kills in dmesg. If the site is slow with memory to spare, the constraint is CPU, disk or a slow query, and more RAM changes nothing.

What are autoloaded options and why do they matter?

Options marked autoload are read on every single page load, including AJAX and REST requests. Plugins that store logs or cached API data there can push the total past several megabytes, which is read and unserialised on every request. Keep it under 1 MB.

Does caching reduce CPU usage on WordPress?

Dramatically. A full-page cache hit is served as a static file: no PHP, no database, no worker consumed. On a site where most visitors are anonymous, page caching typically removes the large majority of CPU work, which is why it is the first thing to fix.

Why does my site use high CPU with almost no visitors?

Usually bots, which do not appear in JavaScript-based analytics but do appear in the server access log. A backlog of scheduled tasks catching up, or a plugin polling an external API on every load, produce the same pattern. Check the access log first.

Put this into practice on G7Cloud

Every site runs in its own dedicated container behind ScaleShield, with daily backups that are restore-tested every night. Start on the free plan, no card needed.

About G7Cloud Engineering

Platform team

Articles written by the engineers who build and run G7Cloud: UK managed hosting and the AI Website Builder. We write about what we operate every day: containers, backups, databases, and the small-business websites that run on them.

More about G7Cloud →