WordPress LCP Optimization: A Step-by-Step Walkthrough
Get WordPress LCP under 2.0 seconds on shared hosting. The exact sequence we run at WitsCode: hero image, fetchpriority, font preload, critical CSS, budget.
The fastest single fix for a failing WordPress LCP score is adding fetchpriority="high" to the hero image and removing loading="lazy" from anything visible in the first viewport. That one change typically cuts Largest Contentful Paint by four hundred to nine hundred milliseconds on shared hosting, because Chrome stops queuing the hero behind stylesheets and webfonts and starts the image download in the same priority lane as the critical CSS. WordPress 6.3 and later adds fetchpriority automatically to the first content image inside the main loop, but theme-rendered hero banners, page-builder background images, and slider first frames still need it set by hand. If your hero is a CSS background-image instead of a real img tag, the fetchpriority hint does not exist for it and you have a deeper problem we will get to.
The second fastest fix is replacing whatever oversized JPEG the theme ships as its default hero with an AVIF or WebP sized to the actual display dimensions and weighing under one hundred kilobytes. Those two changes alone, without a caching plugin or CDN, pull most untuned WordPress sites on Hostinger, Bluehost, or SiteGround from a four-second LCP into the high two-second range. That is still not under two point five, but it stops the Core Web Vitals ranking adjustment from bleeding traffic. Getting under two point zero seconds, which is where we aim for clients at WitsCode, requires the full five-step sequence below run in order. Skipping the diagnostic step is how most WordPress owners spend a weekend optimizing the wrong element.
What LCP measures on WordPress and why hero sections fail it
Largest Contentful Paint records the render time of the largest image, video poster, or block-level text element visible in the initial viewport, measured from when the navigation started. Google considers anything under two point five seconds at the seventy-fifth percentile of real-user page loads to be passing, anything between two point five and four seconds to be in the needs-improvement band, and anything over four seconds to be failing. The metric is part of Core Web Vitals and feeds directly into search ranking signals, so a WordPress site sitting at three point eight seconds is not just slow, it is being actively held down in results pages. The seventy-fifth percentile detail matters because it means three quarters of your visitors need a fast load, not just the one you tested from your office on fiber.
WordPress hero sections fail LCP for a predictable cluster of reasons. The hero image is almost always the LCP element on a marketing page, and almost always too large, in the wrong format, served from origin instead of a CDN edge, marked loading="lazy" by an over-eager optimization plugin, or hidden inside a slider that swaps slides on a JavaScript timer. The hero text, when it is the LCP element, is held back by a webfont that the browser has not yet downloaded and is using the swap font-display behavior to delay paint. The whole above-the-fold region is typically held behind a render-blocking stylesheet that is six hundred kilobytes of unused selectors from a theme designed to do everything. None of these problems show up if you only test from cache, which is why we always test in incognito with cache disabled.
Step one: identify the LCP element before changing anything
Open PageSpeed Insights, paste in the URL, and run the test. Once the report loads, scroll to the diagnostics section and expand the entry called Largest Contentful Paint element. Google will show you the exact selector and the time it took to render. Save that selector. If it is an img tag, you are in the easy case and the next two steps will do most of the work. If it is an h1 or a heading element, your hero text is being held by a webfont and step three matters more than step two. If it is a block-level div with a background image, you have a structural problem that needs the image moved to a real img tag with object-fit cover before any other fix will work, because the browser cannot prioritize a CSS background discovery the same way it can a markup-declared image.
A second diagnostic worth running before you touch anything is the Chrome DevTools Performance panel with throttling set to slow 4G and CPU at four times slowdown. Record a fresh load, find the LCP marker on the timeline, and trace backward to see what the main thread was doing at the moment of paint. On WordPress this almost always reveals one of three things: a render-blocking stylesheet that took eight hundred milliseconds to download and parse, a webfont request that finished after the image was ready, or a page-builder JavaScript bundle that delayed first contentful paint and pushed the LCP behind it. Whichever the trace shows is the one to fix first.
Step two: hero image strategy with fetchpriority, AVIF, and exact sizing
Once you know the hero image is the LCP element, four properties on that one img tag determine ninety percent of its render time. The first is fetchpriority="high", which tells the browser to fetch this image in the same priority class as critical resources rather than the default low class assigned to images. The second is the absence of loading="lazy", which an alarming number of optimization plugins add indiscriminately, including for above-the-fold images where the lazy attribute actively hurts. The third is correct width and height attributes that match the actual display size, both because they prevent layout shift and because they let the browser allocate decode capacity ahead of paint. The fourth is the format and weight of the file itself.
For format, AVIF first with a WebP fallback and a JPEG of last resort gives the smallest file at the same visual quality. A hero displayed at twelve hundred by six hundred pixels should weigh under one hundred kilobytes in AVIF, around one hundred and forty in WebP, and around two hundred and twenty in JPEG at quality eighty. ShortPixel, Imagify, and Smush Pro all generate AVIF and WebP variants and serve them via the picture element. Cloudflare Polish and BunnyCDN Optimizer do the same conversion at the edge without a plugin. The most common mistake we see is a hero uploaded at four thousand pixels wide and downscaled in CSS, forcing the browser to download two megabytes for a twelve-hundred-pixel viewport. Use the WordPress media library size variants, declare the right srcset, and let the browser pick.
Step three: font preload without overloading the head
If the LCP element is text rather than an image, or if your hero is a mix of image and overlaid heading, font loading is the next bottleneck. Webfonts that are discovered in the CSS rather than preloaded in the head face the same priority problem as the hero image used to: the browser does not start fetching them until it finishes parsing CSS, by which point the first paint is already delayed. The fix is a link rel="preload" tag in the head pointing to the woff2 file, with as="font", type="font/woff2", and the crossorigin attribute set, all four of which are required for the preload to work and the browser to actually use the resource it just downloaded.
Preload only the fonts the LCP text element actually uses. Two weights of one family is the realistic maximum, usually a regular and a bold. Preloading every weight a designer specced in Figma saturates the connection with font requests and hurts LCP rather than helps it. Self-host using OMGF or Local Google Fonts so the request goes to your own domain. Subset the woff2 file to Latin or Latin-extended for an English site, which shaves a forty-kilobyte font down to fifteen. Set font-display: swap so the browser paints with a fallback immediately and swaps the webfont in when it arrives. Preload plus swap is what gets a text LCP under two seconds on shared hosting; preload alone with the default font-display of auto often makes things worse because the browser blocks paint waiting for the font.
Step four: critical CSS for the above-the-fold
The fourth step addresses the render-blocking stylesheet that holds the entire above-the-fold region behind a CSS download and parse. The technique is to extract just the rules needed to render the first viewport, inline that subset directly in the head as a style tag, and defer the rest of the stylesheet so it loads asynchronously and applies after the LCP has already occurred. The inlined critical CSS should fit under fourteen kilobytes, which is the size of the first TCP packet and therefore the amount the browser receives in the first round trip. Anything over that size starts costing extra round trips and the technique becomes less effective.
WP Rocket's Remove Unused CSS, Autoptimize with the Critical CSS Power-Up, Perfmatters' built-in generator, and FlyingPress's automated extraction all handle this and update when content changes. SG Optimizer does it on SiteGround hosting without an extra plugin. For sites where the automated extractor produces visual regressions, hand-extraction using the Penthouse npm package or the Critical command-line tool gives a cleaner result but requires a build step. The non-critical CSS should load with the media="print" onload="this.media='all'" pattern, which removes the render-blocking link without needing JavaScript libraries.
Step five: enforcing a hero-section asset budget
The final step is the discipline that keeps the first four from being undone the next time someone adds a plugin or a tracking script. We set an asset budget for everything that has to load before the hero paints, and we measure against it on every release. The compressed HTML for the page should fit under thirty kilobytes, which is enough for any reasonable WordPress page if the theme is not generating bloat. The inlined critical CSS should fit under fourteen kilobytes as discussed. The hero image should weigh under one hundred kilobytes after format optimization. The preloaded fonts should total under sixty kilobytes across both weights combined. There should be zero render-blocking JavaScript before the closing body tag, with all script tags either deferred, async, or moved to the footer.
The total weight to first paint, in this configuration, sits under two hundred and fifty kilobytes. On shared hosting where TTFB alone is often four hundred to seven hundred milliseconds, this budget is what makes a sub-two-second LCP achievable. Going over by two hundred kilobytes does not show up as a clear regression in any single metric, which is why it is easy to drift past it over six months of plugin additions and content updates. A WordPress site without a documented hero-section asset budget will, given enough time, drift back to a four-second LCP regardless of how well it was tuned at launch.
Real before-and-after numbers from shared hosting audits
The numbers from recent client audits illustrate the range of improvement the five-step sequence produces. A Hostinger Premium WordPress plan running an untuned Astra theme with Elementor came in at four point one seconds for LCP, dominated by an unoptimized hero JPEG and a render-blocking Elementor stylesheet. After fetchpriority and AVIF on the hero, font preload for the Inter family, and critical CSS extraction with Perfmatters, the same page measured one point eight seconds. Total work was about three hours, no theme replacement, no hosting upgrade. A Bluehost Choice Plus account running GeneratePress with GenerateBlocks started at three point four seconds and reached one point six seconds with the same sequence applied. The lighter theme was already doing some of the work, which is why GeneratePress and similar block-first themes are the default we recommend for clients targeting Core Web Vitals.
A SiteGround GoGeek site with SG Optimizer already enabled measured two point nine seconds at the start. SG Optimizer handles critical CSS automatically and does a credible job with image conversion, but it does not add fetchpriority to hero images and it does not preload fonts unless told to. Adding both manually pulled the LCP to one point four seconds. The pattern that did not respond to optimization was a Slider Revolution hero on a tourism site that started at five point two seconds. Sliders inherently fail LCP because the slide content is built by JavaScript after the bundle parses, and the LCP element does not exist in the initial HTML. The only fix that worked was replacing the slider with a static hero image, which took the same site to two point four seconds. Sliders and LCP are not compatible, regardless of what the slider plugin promises.
When the fix is the theme, not the tweaks
The five steps above resolve the majority of WordPress LCP failures on shared hosting, but they do not resolve all of them. A theme that ships seven hundred kilobytes of CSS for a single landing page, a page builder that wraps every section in three nested divs with their own animation runtime, a hosting plan with a TTFB over one second, or a hero design that depends on a JavaScript-rendered slider will all defeat the sequence. At that point the fix is not another plugin. The fix is rebuilding the theme on a lighter foundation, switching the page builder, moving to better hosting, or redesigning the hero. We see this often enough at WitsCode that our LCP-targeted WordPress audits start with a thirty-minute call to determine whether the site is fixable in place or whether the team has been optimizing around a structural problem. If you are unsure which case yours is, the audit is the way to find out before another quarter passes with the metric still red.
Get weekly field notes.
Practical writing on shipping products, straight to your inbox. No spam.
Need help with this?
WordPress Development
We design and build web apps, MVPs, and SaaS products. Talk to us about what you are working on.
Talk to usWant to discuss wp speed & core web vitals for your business?
Start a project and we'll talk through where you are, what's working, and the highest-leverage moves for the next 90 days.