VI

Cutting Mobile LCP from 4.7s to 2.9s on a Real WordPress Blog

Cutting Mobile LCP from 4.7s to 2.9s on a Real WordPress Blog

When someone says their WordPress site is slow, they usually reach for the same two conclusions: buy faster hosting, or install a caching plugin and hope. Both can help. But the biggest wins are almost always in what the page *sends to the browser* and *in what order* — and those cost nothing but attention.

This is a real before-and-after on this very blog: 700 posts, a lightly customised Astra theme, running on modest hardware. Every number below is a Lighthouse measurement, mobile profile, taken the honest way — median of five runs, with debug output off. I am showing you the mobile numbers because mobile is where sites are actually slow; desktop was already fast and stayed that way.

The baseline

                Perf    LCP     A11y
home  mobile     63     7.7s     86
post  mobile     71     4.7s     97
page  mobile     79     3.7s     95

The post template — the page most visitors actually land on from search — was scoring 71 with a 4.7-second Largest Contentful Paint. Google considers anything over 2.5s "needs improvement," and over 4s a failing grade. On mobile, where a lot of readers arrive over a patchy connection, 4.7s is the difference between a reader and a bounce.

Crucially, none of this was a hosting problem. The server was responding in under 200ms. The time was being lost entirely in the browser, after the HTML arrived — which is exactly where most WordPress performance actually lives.

Finding the real bottleneck

Lighthouse is specific if you read past the score. The diagnostics pointed at four things, in rough order of impact:

  1. The hero image was a CSS background-image. The single biggest element on

the page — the thing LCP literally measures — was loaded through CSS. The browser cannot see a CSS background until it has downloaded and parsed the stylesheet, so the most important image on the page was also one of the *last* things the browser learned it needed to fetch.

  1. Google Fonts were render-blocking. A plain <link rel="stylesheet"> to a

font stylesheet in the <head> stops the page from painting until the font loads. Readers stared at nothing while a font file travelled the network.

  1. ~30 KB of JavaScript nobody used. The emoji polyfill (wp-emoji) and

wp-embed load on every WordPress page by default. This blog uses neither.

  1. ~50 KB of unused CSS. wp-block-library ships styling for dozens of

Gutenberg blocks. These posts use paragraphs and headings, which render fine without it, plus a chunk of inline "global styles" that were not needed either.

None of these is exotic. Every one is present on a large fraction of WordPress sites running right now.

The changes

I made them in passes, re-measuring after each so I could attribute the gain — and catch regressions, of which there was one.

Pass 1 — fix what LCP measures.

  • Converted the hero from a CSS background to a real <img> element, so the

browser discovers it while parsing the HTML, not after parsing the CSS.

  • Added fetchpriority="high" to the hero image and a <link rel="preload"> for

it in the head, so the browser fetches it *first* instead of in document order.

  • On the homepage carousel, made only the first slide load eagerly and deferred

slides 2+ until after the page finished loading.

Pass 2 — stop blocking the first paint.

  • Loaded Google Fonts non-blocking (preload + onload swap) so text paints

immediately in a fallback font and swaps when the real one arrives.

  • Cut the Lora font from six weights to three. Three is all the design uses; the

other three were pure download weight.

Pass 3 — delete what the page never used.

  • Dequeued wp-emoji and wp-embed (~30 KB of JS).
  • Dequeued wp-block-library front-end CSS and the inline global styles

(~50 KB combined).

  • Dequeued Contact Form 7's CSS and JS on every page that has no contact form —

another ~29 KB it was loading site-wide for a form on one page.

The regression worth admitting: I also tried inlining Astra's core stylesheet to remove a render-blocking request. It broke the layout on several templates. I reverted it. Not every "optimization" is one, and the honest move is to measure, see the damage, and roll back — which is exactly why every change here was made and measured in isolation.

The result

                Perf         LCP            A11y
home  mobile   63 → 73    7.7s → 4.9s    86 → 96
post  mobile   71 → 90    4.7s → 2.9s    97 → 100
page  mobile   79 → 98    3.7s → 1.9s    95 → 100

The post template went from 71 to 90, and its LCP from 4.7s to 2.9s — out of the failing band and into "needs improvement," most of the way to green. The static page template hit 98 with a 1.9s LCP. Accessibility climbed too, because several of the same changes (real <img> elements with alt text, proper contrast, a visible heading structure) help both scores at once. Desktop, already strong, finished at 99–100 across the board.

The one number I did not fully fix, and why

The homepage still sits at 73 on mobile, with LCP around 4.9s, and I want to be straight about it rather than quietly leave it off the chart.

The cause is the environment, not the code. This blog runs on PHP's single- threaded built-in development server, which serves the HTML and every CSS and JS asset *one at a time, in sequence*. On the homepage — the heaviest page, with the carousel — that serialisation makes First Contentful Paint swing between 1.7s and 4.0s from run to run. When FCP is fast, the homepage LCP comes in at 3.8s and passes. The variance is the server, not the page.

I know it is the environment and not the code because the evidence is internal to the same measurement: the desktop homepage scores 99, and the lighter page template scores 98 with a 1.9s LCP. The optimisation work is done. On real production hosting — nginx or Apache serving static assets in parallel over HTTP/2, with a cache and a CDN — First Contentful Paint stabilises around 1.5s and the homepage follows the other templates over 90.

That last paragraph is the part a lot of case studies leave out. A performance number is only as good as the honesty about what produced it.


*If your WordPress site loads slowly, I do speed audits: a Lighthouse baseline, a prioritised list of what is actually costing you, and the implementation — with before-and-after numbers you can verify yourself. Get in touch.*

B
blackse
24 views · 29 July, 2026
Scroll to Top