How to Serve WebP/AVIF Images with Fallbacks in Nginx

Rather than converting every image manually and updating HTML with <picture> tags, Nginx can automatically serve a modern format (WebP/AVIF) when the browser supports it, falling back to the original otherwise — without any HTML changes required.

Prerequisites

  • Nginx installed with the ngx_http_map_module (included in standard Nginx builds)
  • WebP/AVIF versions of your images generated alongside the originals

Step 1 — Generate WebP/AVIF Versions Alongside Originals

for f in /var/www/mysite/html/images/*.jpg; do
    cwebp -q 80 "$f" -o "${f%.jpg}.webp"
done

See How to Optimize Images for Web Performance for the conversion commands in detail. The key requirement here: each photo.jpg needs a corresponding photo.webp at the same path.

Step 2 — Detect Browser Support via the Accept Header

http {
    map $http_accept $webp_suffix {
        default "";
        "~*webp" ".webp";
    }
}

Browsers that support WebP send image/webp in their Accept header — this map checks for that.

Step 3 — Serve the WebP Version When Available and Supported

server {
    location ~* ^/images/(.+)\.(jpg|jpeg|png)$ {
        add_header Vary Accept;
        try_files /images/$1.$2$webp_suffix /images/$1.$2 =404;
    }
}

This attempts to serve the WebP version first if the browser supports it and the file exists, falling back to the original format otherwise — all transparent to the HTML, which just references image.jpg as normal.

The Vary Header (Important for Caching)

add_header Vary Accept tells caches (browser and any CDN in front) that the response differs based on the Accept header — without this, a cache might incorrectly serve a WebP response to a browser/proxy that doesn't support it.

Adding AVIF Support (Preferred Format When Available)

map $http_accept $avif_suffix {
    default "";
    "~*avif" ".avif";
}

location ~* ^/images/(.+)\.(jpg|jpeg|png)$ {
    add_header Vary Accept;
    try_files /images/$1.$2$avif_suffix /images/$1.$2$webp_suffix /images/$1.$2 =404;
}

Tries AVIF first (best compression), then WebP, then falls back to the original — giving every browser the best format it actually supports.

Automating Image Conversion on Upload

For dynamic sites accepting user uploads, integrate conversion into your upload handling code (using an image processing library) rather than a manual batch script, ensuring every new upload automatically gets WebP/AVIF variants generated.

Verifying It's Working

curl -H "Accept: image/webp" -I https://yourdomain.com/images/photo.jpg

Check the response — while the URL still shows .jpg, Nginx should be internally serving the .webp file content if it exists.

Common Errors

WebP never gets served despite browser support — verify the .webp file actually exists at the exact same path/name as the original, and check the map directive is correctly matching the Accept header.

Wrong format served to some visitors after CDN caching — verify the CDN/proxy respects the Vary: Accept header and caches variants separately.

Best Practices

  • Always set the Vary: Accept header when serving format-negotiated content
  • Generate WebP/AVIF as part of your build or upload pipeline, not as a manual afterthought
  • Test with explicit Accept headers via curl to confirm the negotiation logic works correctly

Continue Reading

Browse more articles in Static Site Hosting & Frontend Deployment.

  • webp nginx, avif fallback, image format negotiation, nginx map module
  • 0 istifadəçi bunu faydalı hesab edir
Bu cavab sizə kömək etdi?

Uyğun məqalələr

How to Host a Static Website on a VPS with Nginx

Static websites — plain HTML, CSS, and JavaScript with no server-side processing —...

How to Deploy a Next.js Application on a VPS

Next.js supports several deployment modes — fully static export, server-side rendering with...

How to Deploy a Static Site Built with Astro, Hugo, or Jekyll

Static site generators (Astro, Hugo, Jekyll) produce plain HTML/CSS/JS at build time —...

How to Optimize Images for Web Performance

Images are typically the largest contributor to page weight and load time. This guide covers...

How to Set Up a Jamstack Site with a Headless CMS Backend

The Jamstack architecture (JavaScript, APIs, Markup) combines a pre-built static frontend with a...