Short answer: on WordPress, LCP is almost always the hero image or a slow server response, CLS comes from something arriving late and shoving the layout around, and INP comes from JavaScript you did not write. Fix hosting and the largest image first — that single change moves LCP further than any plugin will.
What are Core Web Vitals, and which one should you fix first?
Core Web Vitals are three measurements of how a page feels to the person waiting for it. LCP asks how long until the main content appears. CLS asks how much the page jumps around while it loads. INP asks how quickly the page answers when someone taps or clicks. Fix LCP first: it is the one most WordPress sites fail, and the one your hosting and images control directly.
The thresholds Google publishes are straightforward — LCP under 2.5 seconds, CLS under 0.1, INP under 200 milliseconds. What trips people up is that these are not the number PageSpeed Insights prints in the coloured circle. That score out of 100 is a lab simulation of one page load on a synthetic connection. Core Web Vitals are field data: what real Chrome users actually experienced on your site over a rolling window. You can score 95 in the lab and still fail in the field, and the field is what counts.
It is also worth being honest about the weight of this. Core Web Vitals are a ranking signal, but a small one — roughly a tiebreaker between pages that are otherwise comparable. Nobody outranks better content by shaving 200 milliseconds. The real argument for fixing them is that people leave slow pages, and a founder who has watched their own signup funnel knows what that costs.
Why is LCP so slow on WordPress?
Because the largest element on the page is nearly always a hero image or a big headline near the top, and WordPress has to go through PHP and a database query before it can serve any of it. Two things dominate: how long the server takes to answer, and how heavy that largest element is.
Server response comes first because nothing else can start until it finishes. On cheap shared hosting with no page cache, every visit rebuilds the page from scratch — queries, plugin hooks, template rendering, all of it. Page caching turns that into serving a file, and it is the single largest improvement available to most WordPress sites. An outdated PHP version quietly adds to the same problem. We cover the hosting and caching side in detail in our guide to speeding up WordPress.
Then there is image weight. WordPress happily accepts whatever came off someone’s phone or out of Figma, and the media library will serve it at full resolution if the theme asks for it. Converting to modern formats is the least glamorous, highest-return work available. When we migrated our own image library to WebP, total image weight came down by roughly half, and on the mobile homepage the payload shrank enough to change how the page felt on a slow connection. Photographic images went to lossy WebP, graphics and screenshots stayed lossless, and the originals were kept as fallbacks. Nobody noticed a quality difference. Everybody noticed the speed.
One trap deserves its own paragraph, because it catches almost everyone. WordPress adds loading="lazy" to images automatically. On every image below the fold that is exactly right. On the LCP image it is an own goal: you are explicitly telling the browser to deprioritise the one thing the metric measures. Exclude the hero from lazy loading, and while you are there, preload it along with the font used in the headline next to it.
What actually causes CLS, and why the usual advice can backfire
CLS is caused by anything that arrives late and pushes content that was already on screen. Images without declared dimensions, embeds that resize themselves, fonts that swap in at a different width, banners injected after the first paint. And one more that almost nobody warns you about: asynchronous CSS.
The worst layout shift we have ever measured was one we caused ourselves, following advice everyone gives. PageSpeed flags your stylesheet as a render-blocking resource, so you load it asynchronously — preload it, then swap it to a stylesheet once it arrives. The page no longer blocks. The warning disappears. You feel clever.
What actually happens is that the browser paints the entire page unstyled, then relays out every element on it the moment the CSS lands. Lighthouse attributes that shift to the <body> element, and the CLS score goes about as bad as a CLS score can go. The technique is not wrong — it only works if you also inline your critical CSS, and we had not. Reverting to a normal blocking stylesheet cost a few milliseconds of first paint and removed the shift entirely.
The lesson generalises past CSS: a warning in a performance tool is not automatically a bug. Blocking on a small, compressed, same-origin stylesheet is cheap. Chasing the warning without doing the other half of the technique trades a tiny measurable cost for an enormous one. Measure the metric you care about before and after, not the length of the recommendations list.
The ordinary causes still deserve attention. Put explicit width and height on every image so the browser can reserve the space. Reserve height for anything injected after load — cookie notices, chat bubbles, review widgets, sticky headers. And if you use font-display: swap, match your fallback font’s metrics to the webfont so the swap does not reflow a whole paragraph.
Why does INP catch WordPress owners off guard?
Because INP measures something the old metric did not. It replaced First Input Delay in 2024, and where FID only timed the delay before the browser started responding to the first interaction, INP measures how long the whole response takes, across every interaction on the page. Sites that comfortably passed FID started failing overnight. On WordPress the delay is rarely the theme — it is plugins and third-party scripts fighting over the main thread.
Start with what loads globally. A depressing number of plugins enqueue their JavaScript on every page whether it is needed or not: form builders on pages with no forms, sliders on pages with no slider, sharing widgets on posts nobody shares. Auditing which plugins load scripts site-wide and dequeuing them where they are not used costs nothing and is usually the biggest win available. Our roundup of WordPress plugins worth keeping is as much about what to remove as what to add.
Then look at the code you ship yourself, because dead weight accumulates quietly. In one audit of our own bundle we found an animation library that was imported and registered but never actually instantiated anywhere, and a slider module bundled for a feature no page used. Removing imports that nothing called cut the bundle by roughly a quarter without changing a single visible behaviour. Nobody had added those on purpose — they were leftovers from features that changed direction.
Third-party embeds are the hardest part, because you cannot optimise code you do not serve. We had a review widget pulling its own webfont from a domain we had no control over, which meant no way to set a font-display policy on it. There is no clever fix for that. The only levers are deferring it until after interaction, loading it on the pages that need it, or deciding it does not earn its place.
How do you measure Core Web Vitals properly?
Use field data, not lab scores. Google evaluates what real Chrome users experienced over a rolling 28-day window, which is what appears in the Core Web Vitals report in Search Console. That report is the scoreboard. Everything else is a rehearsal.
PageSpeed Insights shows both, which is why it confuses people. If your site has enough traffic, the field data sits at the top of the report and the lab simulation below it. If it does not have enough traffic, there is no field data at all, Search Console stays empty, and you are working from lab numbers and judgement — which is fine, as long as you know that is what you are doing.
Two habits make the difference. Test on a throttled mobile connection rather than your desktop on office fibre, because mobile is where WordPress sites actually fail. And change one thing at a time. Swapping the hosting, adding a cache, converting images and deferring three scripts in the same afternoon, then re-running the test, tells you the site got faster and nothing whatsoever about which change did it — or which one quietly made something worse.
What order should you fix them in?
Hosting and caching, then the LCP element, then layout stability, then JavaScript. The order is deliberate: each step makes the next one easier to measure, and the early steps need no design decisions from anyone.
- Hosting, PHP version, page caching. The biggest single lever, and the one that requires no judgement calls. Do it before you touch anything else.
- The LCP element. Modern format, correct dimensions, preloaded, and definitely not lazy-loaded.
- Layout stability. Dimensions on images, reserved space for anything injected late, and no asynchronous CSS unless you are inlining critical CSS too.
- JavaScript. Audit plugin scripts, defer what can wait, delete what nothing calls. Slowest and most fragile, which is exactly why it goes last.
- Re-measure in Search Console after about four weeks. Field data moves slowly. Checking after three days tells you nothing.
If you are running a store, the order shifts: cart fragments and object caching move much earlier, because WooCommerce cannot page-cache the parts that matter most. That is a different job, and we walk through it in our WooCommerce speed guide.
Final thoughts
Core Web Vitals are not a checklist you finish. They are a running measurement of whether your site respects the person waiting for it, and they drift back the moment someone installs a plugin that nobody audits. The sites that hold their numbers are the ones where somebody measures after every release, not the ones that had a good week in March.
The encouraging part is how concentrated the wins are. Hosting, the largest image, and honesty about which scripts earn their place will get most WordPress sites into the green. Everything after that is refinement — and refinement is a much nicer problem to have than a red Search Console report.