WordPress
Slow WordPress site: a technical analysis beyond the usual advice
Why a WordPress site is really slow: TTFB, autoload, queries, hooks, page builders and third parties. A developer's diagnosis, and when caching is no longer enough.
Fabio Folgori8 min read

The same advice on "slow WordPress site" has been around for years: compress images, install a cache, remove a few plugins. It is correct advice, but it stays on the surface. When a WordPress site is structurally slow, the reason almost always lies in how it generates pages and how much work it pushes onto the browser. This is the diagnosis I run, in the order I run it.
Two different problems: server and browser
Slowness has two sources that must be separated straight away.
- Server side: the time PHP and the database take to produce the HTML. It shows in the TTFB of uncached pages and affects LCP.
- Browser side: how much JavaScript and CSS the page has to download, parse and execute. It affects LCP and above all INP.
Mixing them up leads to the wrong fixes: more powerful hosting does not fix an INP ruined by a builder, and an asset optimiser does not fix 800 milliseconds of queries.
Server side: what WordPress does on every request
On every request not served from cache, WordPress loads core, the theme and all active plugins, even those that do nothing on that page. Then it runs the hook chain, reads options and fires the template's queries. Time almost always piles up in the same places.
Autoloaded options
Autoload options are loaded together and can be served from a persistent object cache. In modern versions it is not enough to filter autoload = yes: you have to consider the values recognised by the core version, usually also on, auto-on and auto. Badly uninstalled plugins, transients that never expire and serialised settings hundreds of kilobytes long end up there. I have seen sites with several megabytes of autoload: memory and time paid on every single page. You measure it with a query summing LENGTH(option_value) and clean it up with judgement, not with a "database cleaner" plugin run blindly.
Queries and meta
WP_Query with a meta_query on several keys, ordering by custom fields, LIKE searches on post_content: on wp_postmeta tables with millions of rows they get slow because those fields are not usefully indexed. The real fixes are a better data model (taxonomies where appropriate, dedicated tables for structured data) and a persistent object cache such as Redis so the same queries are not repeated.
Heavy hooks and external calls
A plugin that calls a remote API on init, checks a licence or regenerates something blocks page generation until the response arrives. Query Monitor shows the HTTP requests made during generation: the avoidable ones should be moved off the critical path or served from cache. The necessary calls require timeouts and failure handling.
WP-Cron
By default WP-Cron is triggered in relation to visits and normally starts a non-blocking loopback request. Jobs can still consume shared resources, and timeliness depends on traffic and configuration. For jobs that need regularity I consider a system scheduler, checking first that it works and then disabling the automatic triggering through visits.
Browser side: where INP is lost
A possible culprit is the page builder, to be verified with a performance trace. It generates deep, redundant DOM, loads libraries for animations, sliders and widgets on every page, and adds JavaScript that runs at startup. The result is long tasks on the main thread and interactions that respond late.
The other usual suspects:
- third-party scripts: tag managers with ten tags, chat, maps, pixels, heavy cookie banners. Each competes for the main thread;
- unused global CSS: hundreds of kilobytes of rules for components that do not exist on the page;
- fonts: too many variants, loaded without a suitable
font-displayor from external servers.
"Optimising" here often means adding another plugin that defers, combines or delays scripts. It works up to a point, and meanwhile adds one more layer that can break with every update.
The diagnostic method
- Real data first: Search Console Core Web Vitals (CrUX data) tell you which groups of pages have problems and on which metric. A single lab test is not enough.
- Uncached TTFB, measured on the home page, an archive and a single post.
- Query Monitor on those pages: number and duration of queries, slowest components, HTTP requests, autoload size.
- Browser Performance panel with CPU throttling: long tasks, most expensive scripts, the LCP element and its source.
- Component inventory: for each plugin, what it loads and where. Often half the plugins can be removed or replaced with a few lines of code.
Only after this phase does it make sense to act.
When caching becomes a patch
Page caching is essential on WordPress, but it should be seen for what it is: a way of not running WordPress. If the site is fast only when the cache is warm, the problem is still there: logged-in users pay for it, as do the cart, search, bots requesting uncached pages and every purge after an update.
When I notice that optimisation consists of stacking layers (page cache, object cache, asset optimiser, CDN, a plugin delaying scripts) to compensate for a theme and a plugin set that cannot be touched, that is the sign the problem is architectural.
How I fix a slow site
I follow the diagnostic order above, and every intervention has a criterion: if the bottleneck is a query, a bloated autoload or a plugin calling an API on every request, I fix it in code and the site stays WordPress. If it is a page builder loading everything on every page, it is often better to rewrite the theme with blocks and components loaded only where needed. Optimising is right when the site genuinely needs its admin.
When the answer is to migrate
Often, however, the diagnosis says something else: the site is a set of pages that rarely change, and all of WordPress's weight (PHP, database, plugins, caches stacked on caches) serves to run a panel nobody uses. Everyone wants the panel, few clients really use it. In that case it makes no sense to pay to speed up a machine that should not run on every visit: I generate the pages once and serve them from a CDN. With Nuxt.js (or similar tools, such as Astro or Next.js in static mode) TTFB can drop and the JavaScript can be lightened; LCP and INP still have to be measured, and HTTP caching and CDN configuration still have to be set up: generating the HTML is done in advance, but invalidation, content freshness and client-side JavaScript remain to be managed. How to migrate without losing rankings is covered in From WordPress to Nuxt.js, GitHub and Netlify.
Measuring autoload without changing the database
In a diagnosis of a recent WordPress site I consider the autoload values recognised by the core, not just yes. This read-only query shows the number of options and the size of the stored values; the table prefix must be adapted to the installation:
SELECT COUNT(*) AS options,
SUM(OCTET_LENGTH(option_value)) AS value_bytes
FROM wp_options
WHERE autoload IN ('yes', 'on', 'auto-on', 'auto');
The serialised size is not the same as the memory used in PHP. The core filter can also change the effective set of autoload values, and a persistent object cache can avoid the SQL read on every request. I therefore identify the largest options and their owner before changing them. I do not delete options or transients just because they are large.
Reference: autoload values in WordPress core.
My take
I have always built with WordPress and I know how to make it fast: on hand-written projects, with a custom theme and few plugins, the results are excellent. But for a new site, or a slow one to rebuild, I first ask another question: does it really need a dynamic admin? If yes, I optimise WordPress. If not, the fastest solution is to run nothing on every request. I prefer to say it honestly: it often costs less to rethink the site than to chase scores.
Why work with me
Since 1999 I have delivered more than 300 projects, with more than 30 agencies, from Rome and remotely. I run the diagnosis with a developer's tools, not a score, and tell you whether it pays to optimise or to change course.
If your WordPress site is slow, get in touch: in a call of about 20 minutes we look at the real data and find where the time goes. The service is in Speed and SEO.
Frequently asked questions
Page caching hides generation time for anonymous visitors but does not remove it: logged-in users, the cart, search and uncached pages stay slow. And it does nothing for the work done in the browser, which is where INP is lost.
Only if the server is the bottleneck. You find out by measuring TTFB on uncached pages and profiling requests: if PHP takes 1.5 seconds because of queries and hooks, a more powerful server only helps partially.
Query Monitor for queries, hooks and HTTP requests on the server; Search Console CrUX data for real user experience; the browser Performance panel for long tasks. On important projects, a profiler such as Xdebug or an APM.
When every improvement needs one more plugin, when the theme or builder is the bottleneck and cannot be removed, or when the site does not need dynamic content. Then static generation is often cheaper than endless optimisation.
- #wordpress
- #site speed
- #performance
- #core web vitals
- #database

