WEBPery

Optimise WebP images for fast page loads. Practical guide to quality settings, responsive delivery, lazy loading, and Core Web Vitals.

Make Your WebP Images Load Faster

WebP routinely cuts image weight by 25–35% against JPEG and 50%+ against PNG at visually comparable quality. But the format alone is not the win — the win is in how you encode, size, serve, and load it. This guide walks through every lever that materially affects WebP performance on the public web, with concrete settings you can apply today.

If you have not yet converted your library, start with WebP to PNG, WebP to JPG, or the reverse: PNG to WebP and JPG to WebP. For background on the format itself, see the WebP format overview.

What "optimisation" actually means

Optimising a WebP image is not one decision — it is five, in this order:

  1. Choose the right compression mode (lossy or lossless).
  2. Choose the right quality factor for the visual content.
  3. Serve the right dimensions for the device viewing it.
  4. Deliver the file efficiently (caching, content negotiation, modern protocols).
  5. Load it at the right time in the page lifecycle (lazy loading, priority hints, decoding strategy).

Skipping any one of these undoes the others. A perfectly encoded 4000-pixel hero image shipped to a phone is still slow. A well-sized image loaded eagerly above important content blocks the Largest Contentful Paint. The five levers compound.

1. Lossy or lossless

WebP supports both, and they target different content.

  • Lossy — for photographs, screenshots of real-world content, and any image with smooth gradients. Uses VP8 prediction. Produces dramatic size reductions at quality factors 70–85.
  • Lossless — for logos, icons, line art, and any image with hard edges, flat colour fills, or text overlays. Roughly 25% smaller than PNG at the same fidelity.

If you are unsure which to pick for a given image, read Lossy vs Lossless Compression. As a rule of thumb: if the source is a JPEG, encode as lossy WebP; if the source is a PNG with fewer than 256 colours or has transparency on hard edges, encode as lossless WebP.

2. Quality settings that actually hold up

The single most overshot setting in WebP encoding is the quality factor. Defaults are usually too high.

Use caseLossy q-factorNotes
Hero / above-the-fold80–85Inspected on retina displays; do not go lower
Product imagery75–80Sweet spot for ecommerce
Article body images70–75Almost always indistinguishable from source
Thumbnails ≤ 200px60–70Aggressive compression is invisible at size
Background / decorative50–60Detail loss is acceptable, file size matters

For systematic guidance on settings, see WebP Compression Settings.

A common mistake is encoding everything at q=90 "to be safe." The visual difference between q=80 and q=90 is almost never perceptible on a typical display, but the file size difference is routinely 30–40%.

# Lossy, high quality, slow encoding for best ratio
cwebp -q 80 -m 6 input.jpg -o output.webp

# Lossless, maximum compression effort
cwebp -lossless -z 9 input.png -o output.webp

# Near-lossless: lossy encoding that targets a lossless-like result
cwebp -near_lossless 60 input.png -o output.webp

The -m 6 flag uses the slowest, highest-effort method. It produces measurably smaller files than the default -m 4 at the cost of encoding time — a worthwhile trade since encoding happens once and serving happens millions of times.

3. Serve the right dimensions

A 3000-pixel-wide image sent to a 375-pixel-wide phone wastes bytes that no compression can recover. Responsive images solve this with srcset and sizes.

<picture>
  <source
    type="image/webp"
    srcset="
      /img/hero-400.webp   400w,
      /img/hero-800.webp   800w,
      /img/hero-1200.webp 1200w,
      /img/hero-1800.webp 1800w
    "
    sizes="(max-width: 768px) 100vw, 1200px"
  />
  <img
    src="/img/hero-1200.jpg"
    alt="Descriptive alt text"
    width="1200"
    height="675"
    loading="lazy"
    decoding="async"
  />
</picture>

Generate at least four widths per asset — typically 400, 800, 1200, 1800. The browser picks the smallest file that satisfies the device pixel ratio and viewport. The width and height attributes on the fallback <img> reserve layout space and prevent Cumulative Layout Shift.

If you serve images through a Node pipeline, Sharp is the standard tool:

import sharp from "sharp";

await sharp("source.jpg")
  .resize({ width: 1200, withoutEnlargement: true })
  .webp({ quality: 80, effort: 6 })
  .toFile("output.webp");

The effort: 6 parameter maps to the same encoder effort flag as cwebp -m 6. Set it once in your build pipeline and forget it.

4. Deliver efficiently

Cache aggressively

WebP files are content-addressable: change the file, change the URL. That means you can serve them with Cache-Control: public, max-age=31536000, immutable. Most modern hosts and CDNs apply this automatically to hashed asset paths.

Cache-Control: public, max-age=31536000, immutable

Use the Accept header for negotiation

If you serve images dynamically rather than as static files, inspect the Accept request header. Browsers that support WebP send Accept: image/webp,.... Servers can return WebP to those clients and a fallback format to others without changing the URL — but be sure to set Vary: Accept so caches store separate variants.

Prefer modern transports

WebP delivered over HTTP/3 with Brotli compression for the surrounding HTML is consistently the fastest combination. Modern hosts and CDNs enable both by default; verify with your provider.

CDN-level conversion

Several CDNs (Cloudflare Polish/Mirage, Cloudinary, Imgix, Vercel's image optimisation) will accept JPEG/PNG originals and serve WebP variants automatically based on the request Accept header. This is the easiest path if you cannot retool your build pipeline immediately, though purpose-built builds usually compress slightly better.

5. Load it at the right time

Encoding the perfect 30 KB hero image accomplishes nothing if the browser starts downloading it after the main thread has been blocked for two seconds. Image loading strategy matters as much as image weight.

Lazy load below-the-fold images

<img src="..." alt="..." loading="lazy" decoding="async" />

loading="lazy" defers the network request until the image enters the viewport. decoding="async" lets the browser decode the image off the main thread when it does arrive. Use both on every below-the-fold image. See WebP Lazy Loading for nuance, including why you should never lazy-load your LCP image.

Prioritise the LCP image

For the single most important image on the page — typically the hero or first product photo — do the opposite: tell the browser it matters.

<img src="..." alt="..." fetchpriority="high" decoding="async" />

You can additionally preload it from <head>:

<link
  rel="preload"
  as="image"
  href="/img/hero-1200.webp"
  imagesrcset="/img/hero-800.webp 800w, /img/hero-1200.webp 1200w"
  imagesizes="100vw"
  type="image/webp"
/>

Preload only the LCP image. Preloading multiple images pushes them into contention and slows the one that matters.

Decode hints

decoding="async" is almost always correct. decoding="sync" blocks paint until the image is fully decoded — useful only when you must avoid flashes of unstyled content for a specific element.

Measuring impact

Optimisation without measurement is guessing. The two metrics that matter for image-heavy pages:

  • Largest Contentful Paint (LCP) — should be under 2.5 seconds at the 75th percentile of real user traffic. Hero images are usually the LCP element.
  • Cumulative Layout Shift (CLS) — should be under 0.1. Images without explicit width and height are the most common cause of CLS regressions.

Track these in the field with the Chrome User Experience Report or any real-user-monitoring tool. In development, use Lighthouse and WebPageTest. For the full framework, see Core Web Vitals & Images.

Pitfalls that cost you the win

Even teams that do everything above sometimes leave performance on the table. The recurring traps:

  1. Encoding the same image twice. If your source is already a lossy JPEG, encoding it as lossy WebP at q=85 will look slightly worse than the JPEG it replaced, because you are stacking two lossy passes. Either encode at q=80 (still smaller, still indistinguishable) or keep the JPEG and let a CDN handle conversion.
  2. Shipping WebP without a fallback. WebP support is now over 97% of global browsers, but the long tail still matters for ecommerce. Always use the <picture> element with a fallback <img>. See WebP Browser Support.
  3. Forgetting width and height. Without intrinsic dimensions on the <img> element, the browser cannot reserve space, and you ship a CLS hit every page load.
  4. Optimising icons as WebP. Icons below ~100 pixels are usually better served as SVG. WebP wins for raster content with detail; SVG wins for vector content.
  5. Serving full-resolution images to mobile. Without srcset, even a perfectly compressed image arrives oversized. The srcset attribute is the single highest-leverage performance change on most image-heavy sites.
  6. Caching forever, then changing the file. If you serve image.webp with max-age=31536000 and then overwrite the file in place, stale browsers will hold the old version for a year. Always rename when you re-encode.

Platform-specific optimisation

WebP integration patterns differ by platform. If you are on a specific stack:

Each platform has its own image pipeline and its own optimisation pitfalls — but the underlying principles in this guide apply to all of them.

Where to go from here

The optimisation work compounds: get encoding right, then sizing, then delivery, then load timing. By the time you have walked through all four, image performance is rarely your bottleneck.

WebP on WordPress: Plugins & Optimisation Guide

Add WebP to your WordPress site. Native support, plugin choices, automatic conversion, lazy loading, and serving WebP with fallbacks.

WebP on Shopify: Theme Setup & Image Optimisation

Shopify serves WebP automatically — here's how the CDN works, what theme code you control, and how to optimise product imagery for speed.

WebP in React: Next.js Image & Build Pipelines

Ship WebP in React apps. Next.js Image component, Vite/Webpack pipelines, picture-element patterns, and lazy loading for modern React stacks.

WebP Lazy Loading: Defer Images the Right Way

Lazy load WebP images correctly. Native attribute, IntersectionObserver, priority hints, and the rules that decide which images should never lazy load.