All posts
Performance11 min readMay 26, 2026

How to Pass Google PageSpeed Insights: The Image Optimization Checklist

Most PageSpeed failures trace back to one thing: images. Here is the exact checklist professionals use to move a site from red to green.

ZP

ZeroPNG Team

Editorial

How to Pass Google PageSpeed Insights: The Image Optimization Checklist

The Score Nobody Wants to Show Their Client

You build the site. The design is clean, the fonts are sharp, the client is thrilled. Then someone runs it through Google PageSpeed Insights and the number comes back: 41. Red. The kind of red that ends meetings early.

The frustrating part is not the score. It is the audit list underneath it. A wall of orange and red flags, cryptic labels like "Properly size images," "Serve images in next-gen formats," "Eliminate render-blocking resources." You know images are involved. You do not know where to start.

This guide is the starting point. It is a sequenced, actionable checklist - ten items, ordered by impact, that covers every image-related audit PageSpeed Insights can surface. Work through it top to bottom and a score in the 30s becomes a score in the 90s. We have done it dozens of times. Here is exactly how.

What PageSpeed Insights Actually Measures

Before touching a single image, you need to understand what you are optimizing for. PageSpeed Insights reports two different types of data, and confusing them is a common source of frustration.

Lab Data vs Field Data

Lab data is a simulated page load run by Lighthouse in Google's servers. It loads your page in a headless Chrome instance, throttled to a slow 4G connection with a mid-tier CPU. This is the score that changes immediately when you fix things. It is reproducible and controllable, which makes it useful for iteration.

Field data (labeled "Discover what your real users are experiencing") comes from the Chrome User Experience Report (CrUX), anonymized real-world load times collected from actual Chrome users visiting your site over the past 28 days. Field data cannot be faked, does not move immediately after a deploy, and is what Google actually uses to influence search rankings via Core Web Vitals.

The practical implication: optimize for lab data first, because it is the fast feedback loop. Field data will follow over the next 28-day collection window.

The Four Core Web Vitals

PageSpeed Insights organizes its findings around four metrics that Google has determined predict real user experience:

Metric What It Measures Good Threshold Image Impact
LCP - Largest Contentful Paint How fast the largest visible element loads Under 2.5s High - usually an image
INP - Interaction to Next Paint Responsiveness to user input Under 200ms Low - mainly JavaScript
CLS - Cumulative Layout Shift Visual stability during load Under 0.1 High - unsized images cause shifts
FCP - First Contentful Paint When the first content appears Under 1.8s Medium - render-blocking resources

LCP and CLS are the two Core Web Vitals where images are the primary lever. Get those two right, and your score jumps dramatically. The checklist that follows is ordered to attack exactly those two metrics first.

Why Images Are the Number One Culprit

HTTP Archive data from 2026 shows that images account for an average of 62% of total page weight across the web. On e-commerce sites, that figure rises to 73%. On photography portfolios, it exceeds 90%.

PageSpeed Insights runs nine image-specific audits. On a typical unoptimized site, five or six of them will fire. Each failed audit has a weight in the scoring algorithm. Fix the images, and the score moves more than any other single category of change.

The nine audits are:

  1. Serve images in next-gen formats
  2. Properly size images
  3. Efficiently encode images
  4. Defer offscreen images
  5. Largest Contentful Paint image (not preloaded)
  6. Image elements do not have explicit width and height
  7. Avoid enormous network payloads (image contribution)
  8. Serve static assets with an efficient cache policy
  9. Use video formats for animated content (GIF audit)

The checklist below maps directly to these audits, but organized by the order you should actually fix them, highest impact first.

The Image Optimization Checklist

Item 1 - Preload Your LCP Image

This is the single highest-impact change on the list. On over 70% of web pages, the LCP element is an image, typically the hero banner, a product photo, or a featured article header. If the browser does not discover that image early, every other optimization becomes a rounding error.

The browser's preload scanner reads your HTML before it fully parses it, looking for resources to fetch early. But it can only find images that live in <img> tags. It cannot see images loaded via CSS background-image, JavaScript, or frameworks that inject images dynamically.

Add a preload hint in your <head> to force early discovery:

<!-- Minimum viable preload -->
<link rel="preload" as="image" href="/images/hero.avif"
      fetchpriority="high">

<!-- For responsive LCP images with srcset -->
<link rel="preload" as="image"
      imagesrcset="/images/hero-640.avif 640w,
                   /images/hero-1280.avif 1280w,
                   /images/hero-1920.avif 1920w"
      imagesizes="100vw"
      fetchpriority="high">

Also add fetchpriority="high" directly on the <img> tag:

<img src="/images/hero.avif"
     alt="Hero image"
     width="1920" height="1080"
     fetchpriority="high"
     decoding="async">

What NOT to do: Never put loading="lazy" on your LCP image. It is one of the most common self-inflicted wounds on PageSpeed scores. Lazy loading tells the browser to deprioritize the image, the exact opposite of what you want for LCP.

Impact: In our tests, adding a preload hint for an unpreloaded LCP image drops LCP time by 0.8 to 1.5 seconds on a simulated 4G connection. That single change can push a "Needs Improvement" LCP into the green.

Item 2 - Serve Images in Next-Gen Formats (AVIF / WebP)

This audit fires when PageSpeed detects .jpg, .png, or .gif images that could be smaller if encoded as WebP or AVIF. It estimates the potential savings and flags the specific files causing the issue.

The size comparison at equivalent visual quality:

Format Relative File Size Browser Support (2026) Best For
JPEG Baseline (100%) 100% Fallback
WebP ~70% of JPEG 98%+ General web images
AVIF ~50% of JPEG 95%+ Hero images, product photos

Use the <picture> element to serve AVIF to browsers that support it with a WebP or JPEG fallback for the rest:

<picture>
  <source srcset="/images/photo.avif" type="image/avif">
  <source srcset="/images/photo.webp" type="image/webp">
  <img src="/images/photo.jpg" alt="Product photo"
       width="800" height="600" loading="lazy">
</picture>

To convert your existing JPEG and PNG files to AVIF and WebP, use ZeroPNG's Image Converter. Drop in your originals, select the output format, and download the converted files. Everything runs in your browserm no uploads, no server queue, no per-file fees.

A practical note on AVIF encoding time: AVIF is 5-10x slower to encode than JPEG. Always pre-convert your images during your build step or before uploading to your CMS. Never generate AVIF on the fly for user requests unless you have a dedicated image CDN handling it.

Item 3 - Properly Size Your Images

This audit catches a specific and extremely common mistake: serving a 3000-pixel-wide image that your CSS displays at 400 pixels wide. The browser downloads every byte of the large file, then discards the extra pixels during render. You paid the bandwidth cost for data that was never visible to the user.

The math is brutal. A JPEG photograph at 3000×2000 pixels is typically 1.5 to 3MB. The same photo resized to 800×533 and re-encoded at quality 80 is 60 to 120KB. That is a 20x reduction for the same visual output at typical screen sizes.

The correct approach is to serve different image sizes to different devices using srcset:

<img
  src="/images/product-800.webp"
  srcset="/images/product-400.webp 400w,
          /images/product-800.webp 800w,
          /images/product-1200.webp 1200w,
          /images/product-1600.webp 1600w"
  sizes="(max-width: 600px) 100vw,
         (max-width: 1200px) 50vw,
         800px"
  alt="Product name"
  width="800" height="600"
  loading="lazy">

The sizes attribute tells the browser what display width to expect at each breakpoint, so it can calculate which srcset entry to download. A user on a 375px phone screen downloads the 400w variant. A user on a 2560px monitor downloads the 1600w variant. Nobody pays for pixels they cannot see.

A practical sizing guide for common image types:

Image Type Recommended Max Width Retina Max Width Target File Size
Full-width hero banner 1440px 2880px Under 150KB (AVIF)
Blog post header 1200px 2400px Under 80KB (WebP)
E-commerce product image 800px 1600px Under 100KB (WebP)
Card/thumbnail image 400px 800px Under 30KB (WebP)
Avatar / profile photo 128px 256px Under 8KB (WebP)

Use ZeroPNG's Image Compressor with the Max Width setting to resize and compress in one step. Set max width to your target pixel dimension, set quality to 80, choose WebP or AVIF as output, and process the entire batch in under 30 seconds.

Item 4 - Efficiently Encode Images (Compress Aggressively)

This audit is about compression independently of format. Even a WebP image can be over-large if it was saved at quality 100 out of a photo editor. PageSpeed Insights estimates how many bytes could be saved by encoding each image more efficiently, and flags files where the potential savings exceed 4KB.

The compression sweet spot for photographic images is quality 75 to 85 on a 0-100 scale. Below that range, artifacts become visible at normal viewing sizes. Above it, you are storing data that human eyes cannot perceive, you are paying bytes for mathematical precision that no viewer will ever notice.

For JPEG specifically: the quality setting in consumer software (Photoshop, Lightroom, macOS Preview) is intentionally padded at the high end. "Save for Web" at quality 80 in Photoshop often produces files larger than a dedicated compression tool at quality 80, because the dedicated tool uses more aggressive Huffman optimization. For the same subjective quality, ZeroPNG's compressor typically produces files 15-25% smaller than "Save for Web."

For PNG specifically: the audit usually fires on unquantized 24-bit PNGs. The fix is color quantization, reducing a 24-bit palette (16 million colors) down to an optimized 8-bit palette (256 colors). For most screenshots, logos, and graphics, the visual difference is invisible. File size reduction is typically 60-75%. This is exactly what TinyPNG does on their server, and exactly what ZeroPNG does in your browser using the same underlying algorithm.

Item 5 - Set Explicit Width and Height on Every Image

This one is not directly about file size. It is about CLS, Cumulative Layout Shift which measures how much the page visually jumps while loading. Images without declared dimensions cause layout shifts because the browser has to reserve space for them after the file downloads, which pushes content around.

The fix is two attributes:

<!-- Without dimensions - causes layout shift -->
<img src="photo.webp" alt="Photo">

<!-- With dimensions - browser reserves space before download -->
<img src="photo.webp" alt="Photo" width="800" height="600">

You do not need to match the CSS display size. The browser uses the ratio of width to height to calculate an aspect-ratio box, then scales it to whatever the CSS says. The only requirement is that the ratio matches the actual image.

Pair this with CSS to maintain responsiveness:

img {
  max-width: 100%;
  height: auto; /* overrides the HTML height attribute for fluid scaling */
}

Why this affects your PageSpeed score: CLS is a Core Web Vital. A page with multiple unsized images can score 0.4 or higher on CLS, which puts it firmly in the "Poor" band. Fixing all image dimensions alone can push CLS from 0.4 down to 0.02. That single change often moves an overall score from the 50s into the 70s.

Item 6 - Defer Offscreen Images (Lazy Loading)

Any image below the visible viewport on initial load is an offscreen image. Downloading it immediately is wasted bandwidth, the user has not scrolled there yet and may never scroll there at all. PageSpeed Insights estimates the savings from deferring these images and flags the worst offenders.

Modern browsers support native lazy loading with a single attribute:

<!-- Apply to all images below the fold -->
<img src="product.webp" alt="Product" width="400" height="300"
     loading="lazy">

The browser handles the rest, it starts loading the image when the user scrolls within a threshold of the viewport, typically 1200px away (varying by browser). No JavaScript required.

The critical exception: Never lazy load your LCP image. The LCP image is above the fold by definition. Lazy loading it actively makes your LCP worse by telling the browser to deprioritize it. Apply loading="lazy" only to images that are genuinely below the initial viewport.

A practical rule: the first two or three images on a page get fetchpriority="high". Everything else gets loading="lazy".

For JavaScript-rendered images (React, Vue, Angular): the browser's native loading="lazy" works the same way. If you are using an image component, pass the attribute through. If you are dynamically inserting images via JavaScript, consider an Intersection Observer implementation, which gives you finer control over the loading threshold.

Item 7 - Strip Image Metadata Before Serving

Every JPEG your camera or phone produces comes embedded with EXIF metadata: GPS coordinates, camera make and model, focal length, ISO, timestamp, copyright fields, and often a full-resolution thumbnail. This data is invisible to the viewer but very visible to the file size counter.

EXIF data adds anywhere from 10KB to 150KB to a JPEG file, depending on the camera and any editing software that touched it. Lightroom exports, in particular, can embed extensive metadata including full color profiles and history logs.

Use ZeroPNG's EXIF Remover to strip all metadata from a batch of images before publishing. It processes everything in your browser, no uploads, no server touching your files. For a typical blog post with five photographs, stripping EXIF reduces the total image payload by 50 to 400KB before compression even starts.

This also has a privacy angle: if you are publishing images from a smartphone, the EXIF data contains the exact GPS coordinates where each photo was taken. Most people do not intend to publish their home address alongside their product photos.

Item 8 - Convert Animated GIFs to Video or WebP

The GIF format was designed in 1987. It uses LZW compression, supports only 256 colors, and has no understanding of temporal redundancy (the fact that consecutive frames in an animation often share most of their pixels). A 2MB animated GIF converted to a looping <video> tag with WebM encoding typically compresses down to 100-200KB, a 10x reduction for identical visual output.

PageSpeed Insights fires the "Use video formats for animated content" audit whenever it finds a GIF over a certain size threshold. The fix is to convert to MP4 or WebM and serve via a video element:

<!-- Replace this -->
<img src="animation.gif" alt="Demo animation">

<!-- With this -->
<video autoplay loop muted playsinline width="640" height="480">
  <source src="animation.webm" type="video/webm">
  <source src="animation.mp4" type="video/mp4">
</video>

The autoplay, loop, muted, and playsinline attributes together replicate the behavior users expect from a GIF. Without muted, browsers will block autoplay on mobile. Without playsinline, iOS Safari plays the video fullscreen instead of inline.

Alternatively, you can convert animated GIFs to animated WebP, smaller than GIF, supported by all modern browsers, and usable in a standard <img> tag without the video element overhead.

Item 9 - Avoid CSS background-image for Above-the-Fold Content

The browser's preload scanner, the fast pass that discovers resources before full HTML parsing, cannot see images declared in CSS. A hero banner set as background-image in a stylesheet is invisible to the scanner until the CSS file has been downloaded, parsed, and applied. That adds the entire CSS load time to the image discovery delay, which directly inflates LCP.

The rule is simple: any image that is part of your above-the-fold content should be an <img> element, not a CSS background. This makes it discoverable by the preload scanner and eligible for the fetchpriority and preload hints from Item 1.

<!-- ❌ Browser discovers this image late - bad for LCP -->
<div class="hero" style="background-image: url('/images/hero.avif')">
  <h1>Page headline</h1>
</div>

<!-- Browser discovers this image immediately - good for LCP -->
<div class="hero">
  <img src="/images/hero.avif" alt="Hero" width="1920" height="1080"
       fetchpriority="high">
  <h1>Page headline</h1>
</div>

background-image is still fine for decorative, non-content imagery below the fold - patterns, textures, section backgrounds that are purely aesthetic and not part of the content hierarchy. The rule applies specifically to images that carry content weight and could be the LCP element.

Item 10 - Cache Images with Long-Lived Headers

PageSpeed Insights flags "Serve static assets with an efficient cache policy" when it finds images served without long-lived Cache-Control headers. An image with no cache header, or a short-lived one, is re-downloaded every time the user visits any page on your site, even if the image has not changed in months.

The fix is to serve all static images with a long max-age and use a content hash in the filename to bust the cache when the image changes:

# Nginx config - cache images for 1 year
location ~* .(jpg|jpeg|png|webp|avif|gif|svg)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}
# Apache .htaccess
<FilesMatch ".(jpg|jpeg|png|webp|avif|gif|svg)$">
    Header set Cache-Control "max-age=31536000, public, immutable"
</FilesMatch>

If you are using a CDN like Cloudflare, Vercel, or Netlify, static assets are typically cached at the edge automatically. Check your CDN's cache settings to confirm images are getting Cache-Control: public, max-age=31536000 in the response headers.

The content-hash pattern: When the image changes, rename the file to include a hash of its contents (e.g., hero-a3f9b2.avif). The new URL bypasses the cache automatically. Modern build tools like Vite, Webpack, and Next.js do this automatically for assets processed through the build pipeline.

The Audit Workflow: What Order to Fix Things

Not all fixes are equal in difficulty or impact. Here is a practical prioritization matrix:

Fix Score Impact Time to Implement Do This
Preload LCP image High (LCP) 5 minutes First
Compress all images High (LCP + payload) 30 minutes Second
Convert to WebP / AVIF High (payload) 30 minutes Third
Add width + height attributes High (CLS) 1 hour Fourth
Add loading="lazy" below fold Medium (LCP, payload) 30 minutes Fifth
Resize oversized images Medium (payload) 1-2 hours Sixth
Strip EXIF metadata Low-Medium (payload) 15 minutes With compression step
Implement srcset Medium (mobile LCP) 2-4 hours After quick wins
Convert GIFs to video Medium-High (if GIFs present) 1 hour If PSI flags it
Move hero to <img> tag Medium (LCP discovery) 30 minutes If hero is CSS background
Cache headers Low (repeat visits) 30 minutes Last

The first four items - preload, compress, convert format, add dimensions are achievable in a single afternoon for most sites and will move the score more than everything else on the list combined.

Real-World Example: From 34 to 94

Here is a real before-and-after from a small e-commerce product page - eight product images, a hero banner, and a logo.

Before (Score: 34)

  • Hero banner: 4032×3024 JPEG, 4.8MB - no preload, loaded as CSS background-image
  • 8 product images: PNG screenshots, 1.23-3.1MB each, no compression
  • All images missing width and height attributes
  • No lazy loading on below-fold images
  • No cache headers on any assets
  • LCP: 8.4 seconds | CLS: 0.38

Changes Made (4 hours total)

  1. Compressed all images with ZeroPNG at quality 80, converted to WebP
  2. Resized hero to 1920×1280, resulting in 94KB WebP (down from 4.8MB JPEG)
  3. Moved hero from CSS background-image to <img> tag
  4. Added <link rel="preload"> and fetchpriority="high" to hero image
  5. Added width and height to all nine images
  6. Added loading="lazy" to the eight product images
  7. Added Cache-Control: public, max-age=31536000 for all image paths

After (Score: 94)

  • Hero banner: 94KB WebP - preloaded, in <img> tag with explicit dimensions
  • 8 product images: 35–80KB each, lazy loaded, dimensions set
  • Total image payload: down from 28MB to 640KB (97.7% reduction)
  • LCP: 1.4 seconds ✅ | CLS: 0.02 ✅

No CDN was added. No JavaScript was changed. No server-side infrastructure was touched. The entire improvement came from image optimization and four HTML attributes.

The Fastest Path to Green

If you want a single sentence that covers 80% of PageSpeed image optimization, it is this:

Compress everything, convert to WebP or AVIF, add width and height to every image, preload the LCP image, and lazy load everything below the fold.

That is it. Five steps. The rest of the checklist covers edge cases and further optimizations, but those five will get most sites from a red score to a green one.

PageSpeed Insights will show you exactly which audits are still failing after each iteration. Run the test, fix the top audit, re-run, repeat. The score is not a mystery, it is a receipt for the specific work that remains.

Fix Your PageSpeed Score - Start With Images

Compress, convert, resize, and strip metadata from your entire image library in one session. Free, browser-based, no uploads. Drop your files into ZeroPNG and see the difference in your next PageSpeed run.

Optimize Images Free →

Found this useful?

Share it with someone who needs it.

Share on X