Guide · Performance

Image Optimization Guide: WebP, AVIF, Sizing, srcset, Lazy Loading

Choose between JPEG, PNG, WebP and AVIF, size images correctly, use srcset and lazy loading, and batch convert with cwebp, avifenc and sharp.

By SiteDownCheck Editorial · Updated October 5, 2026 · 6 min read

Images are usually the largest part of a page by bytes, and the hero image is often the element that decides your Largest Contentful Paint. Fixing them is rarely glamorous, but it is dependable. This guide covers which format to use, how to size images for the screen they appear on, how srcset and lazy loading work, and the exact commands to convert a folder of files.

Start by measuring

Do not optimize blindly. Run the page weight analyzer to see how many bytes images contribute and which files are biggest. Then open your browser's Network tab, filter by Img and sort by size. The top five files are normally where most of the gain is. If images are a small share of the page, your time is better spent elsewhere; see the slow site diagnosis flowchart.

Pick the right format

FormatBest forNotes
JPEGPhotographs, universal fallbackLossy, no transparency
PNGScreenshots, flat graphics needing exact pixels or transparencyLossless, large for photos
WebPPhotos and graphics, lossy or lossless, with transparencySupported by all current major browsers
AVIFPhotos where you want the smallest filesOften smaller than WebP at similar quality, but slower to encode; supported by current major browsers
SVGLogos, icons, simple illustrationsVector, scales to any size, compresses well with gzip or Brotli
GIFAvoidUse a short muted looping video instead for animation

How much smaller WebP or AVIF will be depends on the image content and the quality setting you choose, so measure on your own files rather than trusting a headline percentage. The practical rule: serve AVIF or WebP to browsers that support them, with JPEG or PNG as a fallback inside a picture element.

Size images for where they are shown

Format is only half the problem. A 4000-pixel-wide photo shown in a 600-pixel column wastes most of its bytes no matter how well it is compressed.

  1. Find the largest size at which the image is ever displayed, in CSS pixels.
  2. Multiply by 2 for high-density screens. A slot that is 600 CSS pixels wide needs about 1200 pixels of source width for a 2x display. Going beyond 2x rarely adds visible quality.
  3. Resize the source to that width and no larger.
  4. Strip metadata you do not need (EXIF, GPS, color profiles that are not required).

Also set explicit width and height attributes on every img. The browser uses them to reserve space before the file loads, which prevents layout shift and protects your Cumulative Layout Shift score. See the Core Web Vitals guide.

Responsive images with srcset and sizes

A single file cannot suit both a phone and a 4K monitor. srcset lets the browser pick from several widths.

<img
  src="/img/hero-800.jpg"
  srcset="/img/hero-480.jpg 480w,
          /img/hero-800.jpg 800w,
          /img/hero-1200.jpg 1200w,
          /img/hero-1600.jpg 1600w"
  sizes="(min-width: 1000px) 800px, 100vw"
  width="1600" height="900"
  alt="Describe what the image shows">

Here w descriptors tell the browser each file's real pixel width, and sizes tells it how wide the image will be displayed. Without sizes, the browser assumes the image fills the viewport and may download something larger than needed.

To combine modern formats with a fallback, wrap sources in picture:

<picture>
  <source type="image/avif" srcset="/img/hero-800.avif 800w, /img/hero-1600.avif 1600w" sizes="(min-width: 1000px) 800px, 100vw">
  <source type="image/webp" srcset="/img/hero-800.webp 800w, /img/hero-1600.webp 1600w" sizes="(min-width: 1000px) 800px, 100vw">
  <img src="/img/hero-800.jpg" width="1600" height="900" alt="Describe what the image shows">
</picture>

The browser takes the first source whose type it understands, and falls back to the img otherwise.

Lazy loading

Add loading="lazy" to images that start below the visible area:

<img src="/img/gallery-3.webp" width="600" height="400" loading="lazy" decoding="async" alt="Gallery photo">

Two rules matter:

  • Never lazy-load the main above-the-fold image. Delaying your LCP image makes the metric worse. Leave it with default loading and give it fetchpriority="high".
  • Always include dimensions on lazy images, or the page will jump as they load.
<img src="/img/hero-800.webp" width="1600" height="900" fetchpriority="high" alt="Describe what the image shows">

Tooling and commands

cwebp (WebP)

cwebp is part of Google's libwebp tools, available in most package managers (for example the webp package on Debian and Ubuntu).

# Lossy, quality 80, resized to 1200 px wide (height auto)
cwebp -q 80 -resize 1200 0 input.jpg -o output.webp

# Lossless, for graphics with sharp edges
cwebp -lossless input.png -o output.webp

Batch convert a folder:

for f in *.jpg; do cwebp -q 80 "$f" -o "${f%.jpg}.webp"; done

avifenc (AVIF)

avifenc ships with libavif. Option names have changed between releases, so run avifenc --help for your version.

# Quality-based encoding (recent libavif versions)
avifenc -q 60 input.jpg output.avif

AVIF encoding is noticeably slower than WebP, so run it once at build time, not on every request.

sharp (Node.js)

sharp is a widely used Node library for build scripts and image pipelines.

const sharp = require('sharp');

for (const width of [480, 800, 1200, 1600]) {
  await sharp('hero.jpg')
    .resize({ width, withoutEnlargement: true })
    .webp({ quality: 80 })
    .toFile(`hero-${width}.webp`);

  await sharp('hero.jpg')
    .resize({ width, withoutEnlargement: true })
    .avif({ quality: 50 })
    .toFile(`hero-${width}.avif`);
}

ImageMagick

magick input.jpg -resize 1200x -strip -quality 82 output.jpg

On older installs the command is convert instead of magick.

Quality settings

Start around 75 to 85 for WebP and JPEG, and lower for AVIF (its quality scale is not equivalent), then compare visually at full size. Pick the lowest setting where you cannot see a difference on the images that matter most. Photos with fine texture need more quality than flat illustrations.

Let a CDN do it, with caution

Many CDNs and hosting platforms can resize and convert images on the fly and serve WebP or AVIF depending on the browser's Accept header. Check which one yours offers with the CDN detector and your provider's current docs. If you use content negotiation, the server must send Vary: Accept so caches do not hand an AVIF file to a browser that cannot read it. Inspect response headers with the HTTP header checker.

Cache them for a long time

Images rarely change. Give versioned or fingerprinted file names a long cache lifetime:

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

If you cannot rename files when they change, use a shorter lifetime and rely on revalidation. See Cloudflare cache rules and purge if you cache at the edge.

A short checklist

  1. Measure with the page weight analyzer; list the largest images.
  2. Resize to at most 2x the displayed width.
  3. Convert photos to AVIF or WebP, keeping a JPEG fallback.
  4. Use srcset and sizes; set width and height.
  5. Lazy-load below-the-fold images only; prioritize the LCP image.
  6. Cache with long lifetimes and verify headers.
  7. Re-run the speed test and compare.

Next steps

FAQ

Should I replace all my JPEGs with AVIF?

Not blindly. Convert the largest and most visible images first, keep a fallback, and compare quality visually. AVIF encodes slowly, so do it at build time.

Does lazy loading hurt SEO?

Native loading="lazy" is understood by modern search crawlers. Avoid lazy-loading the main above-the-fold image, which can slow LCP.

What image dimensions should I upload?

About twice the largest CSS width the image is displayed at, no more. Provide several widths with srcset for responsive layouts.

Is WebP or AVIF better?

AVIF is often smaller at similar quality but encodes slower. WebP is faster to produce and very widely supported. Using both with a fallback is common.

Get notified when this guide updates

Plus occasional practical tips on uptime and monitoring, nothing else. Unsubscribe anytime.

This page is general information written by our editorial team and updated when documentation changes. Servers and providers differ, so test changes safely first. See our editorial policy and disclaimer.