UI Drafter
Blog

Nginx, AVIF, WebP
Serving the Optimal Image Format

October 2, 2020
Eric Fortis

Why let Nginx determine the best image format to serve?

  • There's no need to change client-side code.
  • Unlike <picture> tags, video posters have no standard way of specifying many URLs for fetching the optimal format supported by the client.

Here's how

Upload the images with the modern extension appended:

  • foo.png
  • foo.png.avif
  • foo.png.webp

For dispatching the most efficient format, Nginx will check the Accept request header. For example, Chrome 85 and up say they support AVIF by sending its MIME type:

Accept: image/avif,image/webp,image/apng,image/*,*/*;q=0.8

nginx.conf 1

http {
  # ...
  map $http_accept $img_ext {
    ~image/avif   '.avif';
    ~image/webp   '.webp';
    default       '';
  }

  server {
    # ...
    location /images {
      add_header  Vary Accept;
      try_files   $uri$img_ext $uri =404;
    }
  }
}

On requests under /images, Nginx defines the custom variable $img_ext with the extension associated to the first matched MIME regex of the map, using the Accept header ($http_accept) as parameter.

Then, try_files checks if there's a file with that extension appended, like foo.png.avif, and serves it. The middle $uri is a fail-safe, for instance, maybe the AVIF isn't uploaded yet.

Lastly, Vary: Accept is set as a response header. This is only needed if the server has caching reverse proxies in front (e.g., intercepting CDNs). Otherwise, those proxies would always serve the same format sent to the first client that requested the particular image.2

References

1. E. Lazutkin (2014) Serve files with Nginx conditionally 2. R. Mulhuijzen (2014) Best Practices for Using the Vary Header