Why Is WordPress Slow, and Which Fixes Actually Move the Numbers?
Most WordPress performance work happens in the wrong order. Hosting and the PHP version move more than every plugin tweak combined, and a lab score will not tell you which is which.
On This Page

A site takes four seconds to show anything. Somebody runs a page speed test, gets a red number, and the fix list writes itself: minify the CSS, combine the JavaScript, install a speed plugin, maybe change the theme. Two weekends later the red number is orange and the site still takes four seconds to show anything, because none of that work touched the reason.
WordPress is not inherently slow. It is a PHP application that assembles a page on request, and it goes slowly when the server is weak, when the request is not cached, when a plugin runs forty queries before the first byte, or when the largest image on the page is a print-resolution photograph. Those four causes account for most of the WordPress performance problems we are asked to look at, and they are almost never what the fix list starts with.
This piece puts the work back in order of effect. It also names the popular advice that is mostly noise, because time spent there is time not spent on the thing that would have worked.
Measure what users feel, not what a lab reports
A synthetic test runs one page, once, on a simulated device, from a data centre somewhere. It is a laboratory instrument and it is genuinely useful for comparing two versions of the same page. It is not evidence about your visitors, who arrive on real phones, over real networks, from real places, with a cookie banner and a chat widget loading over the top.
Start with field data instead: the loading, interaction and layout stability measurements browsers collect from actual sessions. Three of them matter. Largest Contentful Paint asks when the main thing appeared. Interaction to Next Paint asks whether tapping something did anything. Cumulative Layout Shift asks whether the page moved under a thumb. Any serious technical SEO review starts there, because those three are what a visitor experiences as "slow" and a lab score is only a proxy for them.
The second measurement to take is server response time on an uncached request. Load a page nobody has visited recently, or add a query string so the cache misses, and watch how long the server takes before the first byte arrives. That single number separates a hosting problem from a front-end problem, and it takes about a minute to get.
Record field data for your five most important templates, uncached server response time for each of them, and the page weight of the heaviest one. Save it with a date. Without a baseline you cannot tell improvement from weather, and the first thing anybody asks after four weeks of work is what changed.
The WordPress performance fixes, ordered by effect
Everything below is worth doing eventually. The order is what people get wrong, and the order is where the money is. Read this table top to bottom and stop when you run out of budget, rather than starting at the bottom because it is the part you know how to do.
| Fix | Typical effect on real visitors | Effort | What it moves |
|---|---|---|---|
| A host with real CPU and a current PHP version | Large, across every page | Half a day to a week | Server response time, then loading |
| Full-page caching for anonymous visitors | Large, across every page | Hours | Server response time, loading |
| A persistent object cache | Large on query-heavy and logged-in pages | Hours, needs Redis or Memcached | Response time on cart, account and admin |
| Removing a plugin that queries on every request | Large, but uneven | Investigation, then a decision | Response time and interaction delay |
| Correctly sized images in a modern format | Large on media-heavy templates | Hours to days | Loading and layout stability |
| A CDN in front of static assets | Moderate, depends on where visitors are | Hours | Asset delivery for distant visitors |
| Trimming and deferring third-party scripts | Moderate to large | Political more than technical | Interaction delay, main-thread time |
| Preloading the main image, self-hosting fonts | Small to moderate | About an hour | Loading and layout stability |
| Minifying and combining CSS and JavaScript | Small | Minutes, with real breakage risk | Transfer size only |
Hosting and the PHP version
What cheap hosting actually is
Budget shared hosting is a machine with a large number of accounts on it, each one entitled to a slice of processor time. Your PHP process waits its turn. When the site next door gets a traffic spike or runs a backup, your slice shrinks and your visitors wait. This is not a defect. It is the product working exactly as priced.
The symptom is a server response time that is not merely high but erratic: fast at eleven in the morning, unpleasant at nine in the evening, occasionally terrible for no visible reason. If your uncached response time swings by a factor of three across a day with no change in traffic, you have a capacity problem and no amount of front-end work will fix it.
Moving host is disruptive and everybody resists it, which is why it stays at the bottom of the list for months while cheaper fixes are tried. Our position is straightforward: if the response time is the problem, move first and optimise second, because optimising on top of an unreliable server means you can never tell what your changes did. It is also the point at which ongoing website maintenance stops being an expense and starts being the thing that keeps the numbers where you put them.
The PHP version nobody checked
Check which PHP version the site runs on. Installations that have been quietly maintained for years are frequently several major versions behind, because upgrading requires testing and nobody scheduled it. Newer PHP releases execute the same code meaningfully faster and use less memory, so this is one of the very few changes that improves every page at once without touching a line of your own code.
It is not free. An old theme or an abandoned plugin can break on a version bump, which is exactly why it never got done. Stage it, run the site with error reporting on, click through checkout and the forms, then switch. Half a day, and it is the highest-return half day available on most older installations.
Caching, in the order it pays
Full-page cache first
A full-page cache stores the finished HTML and serves it to the next anonymous visitor without running PHP or touching the database at all. For a marketing site, a blog or a brochure site, this is the single biggest change available after hosting, and it turns a dynamic application into something close to a static file server.
Then verify it. Genuinely verify it, by loading a page in a private window and checking the response headers for a cache hit. Plenty of sites have a caching plugin installed, activated, and doing nothing at all because a cookie is set on every visit, or a session starts on every request, or the cache was configured to exclude a path that turned out to be all of them.
Then the object cache
A persistent object cache stores the results of database queries in memory between requests, using Redis or Memcached. It does nothing visible on a fully cached brochure page. It does a great deal on pages that cannot be cached: carts, accounts, search results, filtered archives and the administration screens your editors spend all day inside.
Cart, checkout, account and any personalised page are excluded from full-page caching by design, because serving one visitor another visitor’s cart is a far worse outcome than serving it slowly. So the pages closest to revenue run the uncached path every single time. Measure those separately, and never let a cached homepage convince you the site is fast.
A CDN, for the visitors who are far away
A content delivery network puts copies of your images, stylesheets and scripts on servers near your visitors. If your audience is one country and your server is in that country, the gain is modest. If your audience is spread across continents, it is substantial, and it is mostly a configuration exercise rather than a development one.
The database, and the plugins that abuse it
When an uncached page is slow on a decent server, the database is usually the reason and a plugin is usually the cause. The pattern repeats: an option loaded on every request that has grown to several megabytes, a meta query with no index behind it, a related-posts feature scanning the whole posts table, an analytics plugin writing a row on each page view.
Install a query monitor on staging and load the slow template. You get the count, the time and the origin of every query. Most investigations end within twenty minutes, with one plugin visibly responsible for more than half the work. What happens next is a business decision rather than a technical one, because the plugin usually does something somebody wants.
Stores feel this first and worst. Product filters, variable products, session handling and stock lookups all run uncached and all hit the database, which is why WooCommerce starts to strain at a catalogue size that surprises people. If that is your situation, the work belongs with e-commerce development rather than with a performance plugin, because the fix is usually architectural.
A cache is a way of not doing the work. It is not a way of doing the work faster, and the difference appears on every page you cannot cache.
Images: the usual reason the page feels slow
On most WordPress sites the largest element on screen is a photograph, so the moment that photograph finishes loading is the moment the page feels ready. Which means image handling is not a tidy-up task at the end. It is the front-end fix with the largest effect, and frequently the second-largest fix overall after hosting.
Four things, in order. Serve images at the size they are displayed, not at the size they were uploaded. Use a modern format such as WebP or AVIF with a fallback. Set width and height attributes so the browser reserves the space and the text stops jumping. And do not lazy-load the image at the top of the page, because deferring the very thing that defines the loading measurement makes it worse, which is a mistake almost every automatic plugin configuration makes by default.
Then look at what editors upload. A content team handed an unrestricted media library will upload a twelve-megapixel photograph for a thumbnail slot, every week, forever. That is not carelessness, it is a missing constraint, and the durable fix is a template that decides the size rather than an editor who has to remember it.
Front-end assets, last for a reason
Now the part everybody wanted to start with. Yes, a theme that loads a full icon library, four typefaces, a slider script and a page-builder stylesheet on every route is doing avoidable work. Yes, dequeuing what a template does not use is worth doing. But it is a small change compared with everything above it, and it carries a real chance of breaking the layout.
The exception is third-party JavaScript, which is not small at all. Tag managers, chat widgets, heat maps, testing tools and advertising pixels compete for the same main thread that has to respond when somebody taps a button. This is where interaction delay comes from, and the fix is subtraction rather than configuration. It is also why the choice between Gutenberg, a page builder or custom blocks is a performance decision as much as an editorial one: builders ship generic assets for every possibility, while custom blocks ship only what the block needs.
Fonts deserve a line of their own. Self-host them, load only the weights the design actually uses, and set a display strategy so text renders while the font arrives. It takes an hour, it never breaks anything, and it removes a connection to somebody else’s server from your loading path.
Popular advice that is mostly noise
None of the following is exactly wrong. All of it is oversold, and each item has consumed weekends that would have been better spent on the PHP version.
- Minify and combine everything. Combining files was a workaround for an older HTTP version. On a modern connection it can hurt, and aggressive minification breaks scripts in ways that surface days later.
- Switch to a faster theme. A theme swap is a redesign with a performance story attached. Sometimes correct, never cheap, and it does nothing whatsoever about a slow server.
- Stack three optimisation plugins. They fight. Two caches invalidating each other produce stale pages and an afternoon of confusion. Pick one and configure it properly.
- Lazy-load every image. Below the fold, good. Applied to the hero image, it directly delays the measurement you are trying to improve.
- Clean the database of post revisions. Satisfying, and almost never the bottleneck. Revisions are not queried on a page load. Autoloaded options are, and nobody checks those.
- Chase a perfect lab score. The last stretch of a synthetic score is bought with fragile hacks that field data will not notice. Spend the effort on the checkout page instead.
- Disable the REST API to "harden and speed up". It breaks the block editor and saves nothing measurable. Real WordPress hardening is a different exercise with different settings.
Where we would start on Monday
A working sequence for a site that is slow and nobody knows why. Do these in order and stop when the numbers are acceptable, because there is no prize for finishing the list.
- Record the baseline. Field data for the five templates that matter, plus uncached server response time for each. Dated, saved, shared with whoever will ask later.
- Check the PHP version and upgrade it on staging. The cheapest large win available on any installation that has been running for a few years.
- Confirm the page cache is actually hitting. Response headers, private window, real page. Not the plugin’s own dashboard telling you it is enabled.
- Profile the slowest uncached template. Query count, query time, and which plugin is responsible. Then decide whether the feature is worth what it costs.
- Fix the images on the templates people land on. Right size, modern format, dimensions set, hero image not lazy-loaded and ideally preloaded.
- Audit third-party scripts and remove two. Somebody will defend each one. Ask when it was last used to make a decision, and the argument usually ends there.
- Re-measure after four weeks of real traffic. Field data needs a window to settle. Judging the work the next morning tells you almost nothing.
And the reversal. If, after hosting, caching and images, the site is still fighting you, the honest answer may be that WordPress is no longer the right shape for what you are building, which is the argument in going headless or leaving it behind. A workspace like Acrosite takes the other route entirely: editors work in a structured interface, the files are committed to GitHub, and the deployment runs, so no PHP process sits in front of a visitor at all. Before choosing that, read our note on what happens to your content when you leave a CMS, and our parent company on where drafts live and what stops them.
One concession worth making plainly: a great many WordPress sites are not slow, and the owner has been told they are by a tool that rates a perfectly healthy brochure site in orange. If the field data is fine and visitors are not complaining, the correct amount of performance work is none. If it is not fine, and you want somebody to look at the actual numbers rather than the score, send us what you are seeing.
Common questions.
Why does my WordPress site load slowly even with a caching plugin?
Usually because the cache is not being hit. A cookie set on every visit, a session started on each request, or an over-broad exclusion rule can silently bypass a correctly installed plugin. Check the response headers on a real page in a private window rather than trusting the plugin dashboard. If the cache is hitting and pages are still slow, the delay sits in images or third-party scripts instead.
Does upgrading PHP make WordPress faster?
Yes, usually noticeably. Newer PHP releases execute the same code faster and use less memory, so every uncached request benefits without any change to your own code. The risk is compatibility: an abandoned plugin or an old theme can break on a major version change. Test on staging with error reporting enabled, walk through forms and checkout, then switch the live site over.
How much does hosting affect WordPress speed?
More than any other single factor on an uncached page. Inexpensive shared hosting allocates a small share of processor time per account, so response times swing with whatever else is running on that machine. If uncached server response time varies by a factor of three across a normal day, capacity is the constraint, and front-end optimisation cannot compensate for it.
Should I lazy-load all images on my site?
No. Lazy-loading is right for images below the fold and wrong for the main image at the top of the page, because deferring that image delays the exact moment the page is judged to have loaded. Many optimisation plugins apply lazy-loading everywhere by default, so check the setting, exclude the hero image explicitly, and preload it instead.
What is an object cache and do I need one?
An object cache keeps the results of database queries in memory between requests, typically in Redis or Memcached, so repeated queries are not re-run. It makes little difference on a fully cached brochure page. It matters a great deal on pages that cannot be cached, such as carts, accounts, filtered archives and the administration screens editors use all day.
Is a page speed score the same as real user performance?
No. A synthetic score simulates one device on one connection from one location, which makes it a fair way to compare two versions of a page and a poor description of your audience. Real user measurements are collected from actual sessions across many devices and networks. When the two disagree, trust the field data and treat the lab score as a debugging tool.
Facing this in your
own business?
Tell us where you’re headed — we’ll map the shortest honest route.