Image Compression for Web Performance: A Developer's Guide

Images are the largest contributor to page weight on most websites. This developer-focused guide covers formats, compression, lazy loading, and CDN delivery.

Images dominate page weight

According to HTTP Archive data, images account for roughly 50% of total page weight across the web. The median page transfers about 1 MB of image data. On mobile connections, that translates directly to slower load times, higher bounce rates, and worse Core Web Vitals scores — particularly Largest Contentful Paint (LCP), which is often an image element.

Image compression is therefore one of the highest-leverage performance optimisations available. Unlike JavaScript bundle splitting or CSS minification, which might save tens of kilobytes, proper image optimisation routinely saves hundreds of kilobytes or more per page.

Choose the right format first

Before optimising, ensure you are using the right format for each image. This is the most impactful decision:

  • Photographs: JPEG or lossy WebP. JPEG at quality 80–85 is a good starting point. WebP lossy achieves equivalent visual quality at 25–35% smaller file size. AVIF is newer and achieves even better compression, but encoding is slow and support slightly less complete.
  • Screenshots, UI, logos, diagrams: PNG or lossless WebP. Hard edges and text need lossless encoding to avoid artefacts.
  • Animations: WebP (animated) or MP4 video instead of GIF. Animated GIF is extremely inefficient; a short looping video encoded as MP4 or WebM is typically 5–10× smaller.
  • Icons and simple graphics: SVG where possible. An SVG is infinitely scalable and typically tiny. Only use PNG/WebP for icons when SVG is impractical (complex gradients, photographic elements).

Compression settings that matter

Once you have the right format, apply the right compression settings:

For JPEG, quality 80–85 is the sweet spot for most web photography. Below 70, artefacts become visible. Above 90, file size climbs rapidly with diminishing quality returns. Additionally, strip EXIF metadata (camera model, GPS coordinates, timestamps) — this data is irrelevant for web display and can add 10–50 KB per image. Progressive JPEG encoding is worth enabling for large images: the browser can display a low-resolution version while the full image loads.

For PNG, use maximum DEFLATE compression (level 9) and test multiple prediction filter strategies. Dedicated PNG optimisers routinely save 5–15% over standard encoder output with no quality loss. Strip non-essential metadata chunks.

For WebP, the default quality setting of 75–80 is a good starting point for lossy. For lossless, the "quality" setting in WebP lossless actually controls compression effort (not fidelity) — higher values produce smaller files at the cost of encoding time, with no quality impact.

Responsive images

Serving a 2400px-wide image to a mobile device that displays it at 375px CSS width is a common and costly mistake. The browser downloads all those pixels even though most are never displayed.

Use the srcset attribute to provide multiple sizes and let the browser pick the most appropriate:

<img
  src="hero-800.jpg"
  srcset="hero-400.jpg 400w, hero-800.jpg 800w, hero-1600.jpg 1600w"
  sizes="(max-width: 600px) 100vw, 800px"
  alt="Hero image"
>

Generate at minimum: a 1× version (for the CSS display size), a 2× version (for HiDPI/Retina screens), and optionally a smaller version for mobile. For a hero image displayed at 800px CSS width, serve 800px for standard screens and 1600px for Retina.

Lazy loading

Images below the fold — not visible on initial page load — should be loaded lazily. The browser defers their download until the user scrolls near them, reducing the initial page load significantly.

<img src="below-fold.jpg" loading="lazy" alt="...">

The loading="lazy" attribute is supported by all modern browsers and requires no JavaScript. Do not apply it to the LCP image (typically the hero or above-fold primary image) — lazy loading the LCP element delays the metric the browser is trying to optimise.

The LCP image: prioritise it

The Largest Contentful Paint image should be treated with the opposite of lazy loading: it should load as early as possible. Techniques include:

  • Add fetchpriority="high" to the LCP <img> element.
  • Add a <link rel="preload"> in the document <head> pointing to the LCP image resource.
  • Ensure the LCP image URL is in the initial HTML — not injected by JavaScript after page load.
  • Serve the LCP image from the same origin or a fast CDN to minimise DNS/TLS overhead.

CDN delivery and caching

Serving images from a CDN edge node close to the user reduces latency, especially for users far from your origin server. Most CDN providers also offer on-the-fly image transformations: you can upload a single master image and request resized, reformatted, or compressed variants via URL parameters. This eliminates the need to pre-generate every size/format combination.

Set long cache lifetimes for images (Cache-Control: max-age=31536000, immutable) and use content-hashed filenames so cache busting happens automatically when images change.

Measure, then optimise

Before optimising blindly, measure what is actually slow. Chrome DevTools Network tab shows image download sizes. PageSpeed Insights identifies specific images flagged as oversized. Lighthouse in CI can catch regressions automatically.

The biggest wins usually come from: right-sizing images for their display dimensions, switching photographs from PNG to WebP/JPEG, and stripping metadata. Lossless recompression of already-reasonable files is worth doing but produces smaller relative gains.

To compress images without quality loss as a first step, try compressanimage.com — it handles PNG, JPEG, and WebP with format-appropriate lossless optimisation.

web performanceCore Web VitalsLCPimage optimisationCDN

Ready to compress your images without losing quality?

Try the free tool →