Serving alpha channel images via nginx

by Mark Somerville

We wanted to serve images with an alpha channel, using the most efficient format that each browser can handle. Here's one pretty clean way that avoids use of the `if` directive in nginx (if is evil[1]).

First things first, let's ensure nginx knows how to serve all the images we want to send. I added this to the mime.types file that nginx reads:

image/vnd.ms-photo                    jxr wdp hdp;
image/jp2                             jp2;

Then, in a simple nginx.conf:

http {
    include mime.types

    ...

    map $http_accept $webp_extension {
        "~*webp" ".webp";
    }

    map $http_accept $png_extension {
        "~*png" ".png";
    }

    map $http_accept $wdp_extension {
        "~*jxr" ".wdp";
        "~*wdp" ".wdp";
    }

    server {
        listen       80;
        server_name  localhost;

        root   /data/alpha_images;

        location / {
            index  index.html index.htm;
        }

        location ~ ^(?<image>.+)\.image$ {
            try_files $image$webp_extension
                      $image$wdp_extension
                      $image$png_extension
                      $image.jp2
                      =404;
        }
    }
}

This all works quite nicely by simply making a single request to /images/hello.image. `map` creates a new variable based on certain conditions.  In our case, we set a few variables based on if the requesting browser advertises support for a certain format. If it doesn't, the variable isn't set.

Most browsers are pretty good about advertising what they accept using the HTTP accept header. Unfortunately Safari doesn't, but we are lucky that Firefox declares that it accepts PNGs (other browsers aren't always explicit about that format) so we can use a little `try_files` trickery to use JPEG2000 as the last resort that Safari receives (`try_files` looks on the filesystem and serves the first file in the list that it finds). Happy days.

All the other browsers get the best image they declare that they can accept thanks to setting the variables in the `map` statements above. Come on Apple, get your shit together with Safari. You do realise you are the new Internet Explorer, don't you?

1. - https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/

Mark Somerville

Mark Somerville

I play with computers and do other things.