Most Core Web Vitals projects fail not because the concepts are hard but because simple mistakes compound into bigger problems. I've reviewed hundreds of optimization attempts in support tickets, and the same errors show up over and over.
The fixes themselves work fine when applied correctly. The problem is execution. You can follow a tutorial step-by-step and still tank your scores if you make one of these mistakes.
Mistake 1: Optimizing without measuring first
Running PageSpeed Insights once and calling it your baseline is not measurement. Real-world users experience your site differently than a lab test from a data center.
You need field data from actual visitors before you change anything. Open Google Search Console, go to Core Web Vitals, and check the status for mobile and desktop. If you don't have 28 days of field data yet, wait. Lab scores from Lighthouse are useful for debugging specific issues but terrible for setting priorities.
The correct approach: measure for at least two weeks in Search Console or use the Chrome User Experience Report to see real percentile distributions. Only then do you know which metric needs work and which pages hurt you most. I've seen clients waste weeks optimizing desktop LCP when their actual problem was mobile CLS on product pages that never showed up in their test runs.
Mistake 2: Lazy loading above-the-fold images
This one kills LCP every time. You added a lazy loading plugin or sprinkled loading="lazy" on every image tag because someone said it improves performance. It does improve performance—for images below the fold.
When your hero image or main content image uses lazy loading, the browser waits until JavaScript decides to load it. That delay adds hundreds of milliseconds to your Largest Contentful Paint. The image is right there in the viewport, but you told the browser to ignore it until later.
<!-- Wrong -->
<img src="hero.jpg" loading="lazy" alt="Hero">
<!-- Right -->
<img src="hero.jpg" fetchpriority="high" alt="Hero">
Set fetchpriority="high" on your LCP image instead. Better yet, preload it in your document head if it's above the fold on every page.
<link rel="preload" as="image" href="/hero.jpg" fetchpriority="high">
Check what your LCP element actually is in PageSpeed Insights before you make changes. Often it's not the image you think.
Mistake 3: Compressing images until they look bad
You ran every image through a compression tool at maximum settings and now they look terrible on retina displays. Or you converted everything to WebP without keeping fallbacks and broke images in older browsers.
Image optimization is about finding the balance between file size and visual quality for your specific use case. An e-commerce site showing product details needs higher quality than a blog's decorative thumbnails. Aggressive compression helps LCP only if you don't lose so much quality that users bounce.
Serve responsive images with srcset so mobile users get appropriately sized files:
<img
src="product-800.jpg"
srcset="product-400.jpg 400w, product-800.jpg 800w, product-1200.jpg 1200w"
sizes="(max-width: 600px) 400px, (max-width: 1200px) 800px, 1200px"
alt="Product name"
>
Test your compressed images on an actual phone screen, not just your desktop browser zoomed out. If the quality looks poor there, dial back the compression.
Mistake 4: Loading third-party scripts synchronously
You have Google Analytics, Facebook Pixel, a chat widget, and three marketing tags all loading in the document head with regular script tags. Each one blocks parsing until it downloads and executes.
Third-party scripts are the top cause of poor INP scores. They run JavaScript on the main thread and delay user interactions. Loading them synchronously also hurts LCP by blocking the critical rendering path.
Move non-critical scripts to async or defer:
<!-- Wrong -->
<script src="https://example.com/analytics.js"></script>
<!-- Right for order-independent scripts -->
<script async src="https://example.com/analytics.js"></script>
<!-- Right for scripts that need DOM ready -->
<script defer src="https://example.com/widget.js"></script>
Even better, delay loading these scripts entirely until after the page is interactive. Use a simple timeout or wait for user interaction:
window.addEventListener('load', function() {
setTimeout(function() {
// Load non-critical scripts here
}, 3000);
});
Google Tag Manager makes this mistake easy to fix—use custom triggers to delay tag firing instead of loading everything on page view.
Mistake 5: Not fixing render-blocking CSS
Your theme loads five CSS files, a Google Font, Font Awesome icons, and a separate mobile stylesheet. All of them block rendering because the browser won't paint anything until it has all the styles.
Inline your critical CSS directly in the HTML head and defer everything else. Critical CSS means the styles needed to render above-the-fold content—usually just layout, typography, and your hero section.
Tools like Critical or Penthouse can extract critical CSS automatically, but honestly for a simple site you can do it manually. Copy the styles for your header, hero, and first content section into a <style> block in your head. Then load the full stylesheet with media print and switch it on load:
<link rel="stylesheet" href="/style.css" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="/style.css"></noscript>
Google Fonts deserve special attention. Instead of loading multiple weights and styles, pick only what you actually use. Two weights and one style is usually plenty.
So what about layout shifts during optimization?
Mistake six is the one that frustrates me most in support requests.
Mistake 6: Not reserving space for dynamic content
You fixed your images by adding lazy loading, but you forgot to set width and height attributes. Now every image causes a layout shift when it loads. Same problem with ads, embeds, and any content injected by JavaScript.
Cumulative Layout Shift measures visual stability. Every time an element loads and pushes content down the page, CLS increases. The fix is simple but tedious: reserve space for every dynamic element before it loads.
For images, always set explicit dimensions:
<img src="photo.jpg" width="800" height="600" alt="Photo">
Modern browsers use aspect ratio to reserve space even when the image is responsive. For ads and embeds, wrap them in a container with a fixed aspect ratio:
.ad-container {
position: relative;
width: 100%;
padding-bottom: 56.25%; /* 16:9 ratio */
}
.ad-container > * {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
Web fonts cause layout shifts too when they load and change the text dimensions. Use font-display: swap with a fallback font that has similar metrics:
@font-face {
font-family: 'CustomFont';
src: url('/font.woff2') format('woff2');
font-display: swap;
}
Mistake 7: Caching everything without testing
You enabled aggressive caching in your host's control panel or installed a caching plugin with default settings. Now logged-in users see stale content, forms break, and dynamic features don't work.
Caching absolutely helps with Core Web Vitals by reducing server response time and speeding up repeat visits. But caching the wrong things creates problems worse than slow load times.
Never cache: - User-specific content (account pages, shopping carts) - Pages with forms that use nonces or CSRF tokens - Search results - Any endpoint that returns different content based on cookies
Most caching plugins let you exclude URLs by pattern. For WordPress, exclude these at minimum:
/wp-admin/
/cart/
/checkout/
/my-account/
Set appropriate cache lifetimes too. Static assets like images and CSS can cache for a year. HTML should cache for a much shorter time, maybe an hour or a day depending on how often you update content.
Mistake 8: Ignoring server response time
You optimized every front-end asset but your Time to First Byte is still terrible because the server takes two seconds to generate the HTML. That slow TTFB delays everything else—LCP can't start until the browser receives markup.
Check your server response time in the Network tab of your browser's developer tools. Look at the first document request and check the "Waiting (TTFB)" time. If it's over 600ms, your server is the problem.
Common causes: - No server-side caching (install Redis or Memcached) - Slow database queries (add indexes, optimize queries) - Underpowered hosting (upgrade to more CPU/RAM) - External API calls in the rendering path (move them to async)
For WordPress specifically, install a caching plugin that does full-page caching like WP Rocket or W3 Total Cache. The first request will still be slow, but cached requests will be fast.
If you're on shared hosting and TTFB stays high even with caching, you've outgrown your plan. VPS or managed WordPress hosting will help.
Mistake 9: Testing on fast connections and devices
You ran all your tests on a MacBook Pro over office fiber and everything looks great. Then you check Search Console and see that 60% of mobile users have poor Core Web Vitals.
Real users are on phones, often on LTE or slower connections. Your site feels fast to you but not to them. This is why field data matters more than lab scores.
Test on an actual mid-range Android phone over a throttled connection. Chrome DevTools has device emulation and network throttling built in, but nothing beats a real device. If you don't have one, use WebPageTest and select a mobile device with 3G connection.
The performance profile changes dramatically. Scripts that execute in 50ms on your laptop take 500ms on a budget phone. Images that seem fine suddenly take seconds to decode. Focus your optimization on the worst-case scenario, not the best.
What to check first
Start with Search Console to see which metric fails for real users. Then open PageSpeed Insights and look at the opportunities section—it'll tell you exactly which resources block rendering or slow down LCP.
Most of these mistakes are obvious once you know what to look for. The hard part is methodically going through your site and fixing each one without breaking something else. Test every change in staging first, especially caching rules and script modifications.
Core Web Vitals optimization works when you measure accurately, fix the right things, and verify the results with actual user data. Skip any of those steps and you'll waste time optimizing the wrong problems.
