Back in 2022, a hosting comparison site landed in my inbox. Big catalogue — around 4,000 pages, deeply nested category structure, affiliate links scattered everywhere, hero images the size of small continents. Their Google Search Console was a horror show. LCP sitting at 4.2 seconds on mobile. INP (then still called FID) borderline. The client had already spent £6,000 with a previous agency who'd thrown a CDN at it and called it a day.
That project is what eventually shaped how we think about performance at Seahawk for high-page-count sites — directory sites, listing platforms, hosting review properties like HostList-style builds. What follows is the actual playbook.
---
Why Large Sites Break LCP Differently
Small sites have simple problems. One hero image, one theme, one plugin doing too much. Fix those three things and you're under 2 seconds.
Large sites? Different animal entirely. The LCP element changes depending on which page you're on. A category archive has a different LCP candidate than a product detail page, which has a different candidate than the homepage. Most performance tools report a single score. That number is a lie — it's an average of a wildly varied set of pages, and fixing the homepage while ignoring the 3,800 listing pages underneath it is exactly how agencies earn bad reputations.
The Core Web Vitals documentation from Google is clear that field data — what real users experience, measured in Chrome User Experience Report — is what Google actually uses for ranking signals. Lab data from PageSpeed Insights is directional, not definitive. I've seen sites score 95 on PSI and still have Poor LCP in CrSX data. Don't confuse the two.
The Three Real Culprits on Listing Sites
On every large site I've audited (and I've been through hundreds at this point), LCP failures trace back to three root causes almost every time:
- Unoptimised hero or card images — often served at full resolution, wrong format, no
fetchpriorityhint - Render-blocking third-party scripts — affiliate trackers, ad networks, comparison widgets loading before the browser can paint anything
- TTFB inflating the LCP budget — slow server response eating 600–900ms before the page even starts loading
That third one is the one people underestimate. If your TTFB is 700ms, you've already spent nearly half your "Good" LCP budget before the browser has rendered a single pixel. Hosting choice and server-side caching aren't DevOps concerns — they're SEO concerns.
---
Start With TTFB: It's a Hosting and Caching Problem First
The HostList-type project I mentioned above? First thing I did was run WebPageTest from five different geographic locations. TTFB was consistently 680–820ms. The site was on a shared host in Virginia. Most of their organic traffic came from the UK and Germany.
Moved them to a managed WordPress host with edge nodes in London and Frankfurt. Configured full-page caching with cache lifetimes of 12 hours on listing pages (the data didn't change that often anyway). TTFB dropped to 120–180ms on repeat requests, 280–350ms on first-hit cache misses. That single change shaved roughly 500ms off LCP before I touched a single image.
A few things I always check on the hosting side:
- Is full-page caching actually working? Check the
X-Cacheresponse header. If it saysMISSon every single request, your caching layer isn't functioning. - Is the origin server geographically close to your primary audience? This sounds obvious. You'd be surprised how often it's wrong.
- Is keep-alive enabled? Some cheaper hosts still disable persistent connections. Wild in 2024, but it happens.
- Is HTTP/2 or HTTP/3 active? Run
curl -I --http2 https://yourdomain.comand check the protocol in the response.
Don't touch image optimisation until you've sorted TTFB. Optimising images on a slow server is like painting a house that's on fire.
---
The LCP Image Problem (And Why `fetchpriority` Changes Everything)
Right. Images. On a listing site with card-grid layouts, the LCP element is almost always the first above-the-fold card image — or the hero banner. The browser has to discover it, fetch it, decode it, and render it. Each of those steps can be delayed.
Here's what we actually did on that HostList project, in order:
- Converted all card images to WebP — using Imagify's bulk conversion. Average file size dropped 58% without visible quality loss at the card thumbnail sizes we were using (280×180px display, served at 2x for retina).
- Added `fetchpriority="high"` to the first card image — This one change alone knocked ~200ms off measured LCP in WebPageTest. The browser stops treating it like a regular lazy image and queues it immediately in the preload scanner.
- Removed `loading="lazy"` from the first two rows of card images — Lazy loading is brilliant for below-fold images. On the first visible row, it actively hurts you. It tells the browser to not fetch the image until it's near the viewport — which it already is.
- Added a `<link rel="preload">` tag for the hero image in the
<head>on the homepage template specifically.
That sequence brought the homepage LCP from 3.1s to 1.4s in lab conditions. Field data followed within about 28 days (that's roughly how long it takes CrUX data to reflect changes at scale).
A Note on Responsive Images
If you're serving the same 1400px-wide image to a mobile device, you're burning bandwidth and adding decode time. Use srcset properly. I know this sounds like a 2016 conversation but I still see it on probably 40% of the sites that come through Seahawk. The WordPress wp_get_attachment_image() function generates srcset automatically — but only if the image was uploaded at sufficient resolution and the theme hasn't suppressed it with add_filter('max_srcset_width', ...).
---
Render-Blocking Scripts: The Affiliate Site Tax
Hosting comparison sites and listing platforms live on affiliate revenue. That means third-party tracking scripts. Commission Junction, Impact, Awin, custom pixel trackers — they pile up. I've counted 14 separate third-party script origins on a single listing site. Each one is a separate DNS lookup, TCP connection, and TLS handshake before a single byte of that script is received.
The fix isn't to remove the scripts. You can't — the revenue depends on them. The fix is sequencing.
No third-party script should ever block the initial render of the LCP element. Full stop.
Practically, this means:
- Move all affiliate/analytics scripts to load after the
DOMContentLoadedevent, not in<head>. - Use
asyncordeferattributes on every script you control. - For scripts you don't control (injected by tag managers), load Google Tag Manager itself with
defer— yes, this is safe for most affiliate tracking use cases, and GTM's own documentation acknowledges this approach. - Use a script manager like Asset CleanUp Pro or WP Rocket's script delay feature to defer non-essential third parties until user interaction (first scroll or first click).
On the HostList rebuild, deferring third-party scripts reduced Total Blocking Time from 1,840ms to 290ms. TBT isn't a Core Web Vital directly, but it correlates strongly with INP, which is.
---
Font Loading: The Silent LCP Killer Nobody Talks About
Custom fonts cause a specific failure mode. The browser renders your layout, reaches the LCP text element (sometimes LCP is a heading, not an image), and then waits for the font file before painting it. This is called Flash of Invisible Text, and it delays LCP by anywhere from 200ms to over a second depending on font file size and server proximity.
Two things fix this:
font-display: swapin your@font-facedeclaration — the browser renders in a fallback font immediately and swaps when the custom font loads. LCP candidate gets painted on time.- Self-host your fonts. Google Fonts adds a cross-origin request. Self-hosting with the google-webfonts-helper tool lets you serve fonts from your own domain, cutting that extra connection.
I converted a large directory site off Google Fonts in about 45 minutes using that tool. LCP improved by 180ms. Not transformative alone, but combined with everything else, these margins compound.
---
Measuring What Actually Matters in the Field
CrUX data is the ground truth. But it only updates monthly and only for URLs with sufficient traffic. For large sites with thousands of pages, you need something more granular.
I use PageSpeed Insights API scripted across a representative sample of URLs — typically the top 100 pages by traffic, 50 category-level pages, and 20 "thin" deep-catalogue pages. Running this monthly gives a proper performance distribution, not a single-point score.
In Lighthouse CI (which we run in CI/CD pipelines for clients who have dev cycles), we assert against:
- LCP ≤ 2.5s in lab (conservative target, since field tends to lag lab by 10–15%)
- TBT ≤ 300ms
- CLS ≤ 0.1
But honestly — for a HostList-type build where the goal is sub-1.5s LCP — lab targets need to be tighter. We set LCP ≤ 1.8s in Lighthouse CI for those projects, which typically produces 1.3–1.5s in field data once CrUX catches up.
---
Putting It Together: The HostList Result
After running through all of the above — hosting migration, full-page caching, image format conversion, fetchpriority on LCP candidates, lazy-load removal from above-fold rows, script deferral, and font self-hosting — here's what the numbers looked like:
- TTFB: 780ms → 160ms (median, UK visitors)
- LCP (lab, mobile): 4.2s → 1.4s
- LCP (field, CrUX, 75th percentile): 3.8s → 1.6s (measured 60 days post-migration)
- TBT: 1,840ms → 290ms
- CLS: was already fine at 0.03, unchanged
Not every site will get a 2.8-second improvement. But almost every large listing site I've worked on has had all of these problems simultaneously, which means the gains are stacked. Fix one thing and you get 300ms. Fix all of them and you get 2.5 seconds.
---
FAQ
Does hosting really matter that much for LCP?
Yes — probably more than anything else at the start. If your TTFB is above 500ms, no amount of image optimisation will get you to "Good" LCP. TTFB is the foundation everything else sits on. Get it under 200ms first, then worry about the rest.
Should I use a CDN instead of migrating hosts?
A CDN helps with static asset delivery and can reduce TTFB for cached full-page HTML if configured properly. But many CDN setups only cache assets, not full HTML responses. Check whether your CDN is actually serving cached HTML or just offloading images. If it's the latter, a better origin host will do more for LCP.
Is `fetchpriority="high"` widely supported now?
As of 2024, yes — it's supported in Chrome, Edge, and Safari (since Safari 17.2). Firefox support landed in Firefox 132. For browsers that don't support it, it's safely ignored. There's no downside to adding it to your LCP image element.
How long does it take for CrUX data to reflect improvements?
Roughly 28 days from when real users start experiencing the faster version of the site. CrUX uses a rolling 28-day window. So if you deploy changes today, your field data score won't fully reflect them for about a month. Don't panic if PageSpeed Insights still shows "Needs Improvement" the day after your optimisation sprint.
What's the single highest-impact change for a listing site?
On nearly every project, it's been TTFB. But the second-highest has consistently been removing loading="lazy" from above-fold card images and adding fetchpriority="high". Together those two things usually account for 40–60% of the total LCP improvement. Everything else is compounding gains on top of that foundation.
---
Performance work on large sites is mostly plumbing. Unglamorous, methodical, and deeply satisfying when you pull a 4-second LCP down to 1.4 seconds and watch organic traffic lift two months later. There's no single magic setting — just a sequence of specific decisions made in the right order.
Get the server fast. Then get the LCP element discovered and fetched early. Then get everything else out of its way.
