2

Not sure if I'm trying to pull off the impossible, but I'm wanting to configure Nginx to serve ".webp" files to browsers that support the file format and serve fallback files (i.e. pngs, jpgs, etc.) to browsers that don't yet support the WebP format. I would also like for Nginx to do this all while checking for retina support and serving the original or @2x sized versions of those photos.

Sample process for an image called 'sample-photo' in the /assets/img/ directory:

  • Check if browser supports WebP AND if it is a hi-res device, serve: "assets/img/sample-photo@2x.webp"
  • Device may not be hi-res, so instead serve: "assets/img/sample-photo.webp"
  • Maybe WebP is not supported, but device is hi-res so serve: "assets/img/sample-photo@2x.png"
  • Browser does not support WebP and device is NOT hi-res, serve: "assets/img/sample-photo.png"

I've taken a stab at this myself, but am not having much luck. I found two awesome posts that have helped me start in the right direction. Below's my code. Would really appreciate any insight and help on this. My first thought is maybe something wonky is going on in the last try_files statement. Thanks so much!

  1. Conditionally serving high resolution images
  2. Serve files with nginx conditionally

This code is in the HTML head of my document. Checks for hi-res support and sets a cookie:

<!-- Set the 'device-pixel-ratio' cookie with Javascript if hi-res is supported -->
<script type="text/javascript">
    if (!document.cookie.match(/\bdevice-pixel-ratio=/)) {
        document.cookie = 'device-pixel-ratio='
            + (window.devicePixelRatio > 1 ? '2' : '1') + '; path=/';
    }
</script>

<!-- A CSS-only fallback of setting the 'device-pixel-ratio' cookie just in case browsers have Javascript disabled -->
<style type="text/css">
    @media only screen and (-webkit-min-device-pixel-ratio : 2),
        only screen and (min-device-pixel-ratio : 2) {

        head {
            background-image: url(/set-device-pixel-ratio/2);
        }
    }
</style>

This is most of my base 'nginx.conf' file. Left some parts out for brevity:

user www-data;
http {
    ##
    # Basic Settings
    ##
    sendfile on;

        include /etc/nginx/mime.types;
        default_type application_octet-stream;

        ##
        # Gzip Settings
        ##
        gzip on;
        gzip_disable "msie6";
        # gzip_vary on;
        # gzip_proxied any;
        # gzip_comp_level 6;
        # gzip_buffers 16 8k;
        # gzip_http_version 1.1;
        # gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript

        ##
        # Conditional variables--
        # Define a variable called "$webp_suffix" if the HTTP Accept header contains the "webp" substring
        # and populate it with 'webp', otherwise leave empty.
        ##
        map $http_accept $webp_suffix {
            default     ""
            "~*webp"    ".webp";
        }
}

Lastly, this is the server block I have created for my site:

server {
    listen 80;
    server_name localhost;
    root /var/www;
    index index.html index.htm;

    location / {
        try_files $uri $uri/ =404;
    }

    # Sets the device-pixel-ratio cookie
    location ~ /set-device-pixel-ratio/(/d+)/? {
        add_header Set-Cookie "device-pixel-ratio=$1;Path=/;Max-Age=31536000";
        return 204; # nginx does not allow empty 200 responses
    }

    # Serve appropriate image assets
    location ~(/assets/img/[^\.]+)(\.(?:jpg|jpeg|png|gif))$ {
        # Naming convention for hi-res images:
        set $hidpi_uri $1@2x$2;

        if ($http_cookie !~ 'device-pixel-ratio=2') {
            break;
        }

        try_files $hidpi_uri$webp_suffix $uri$webp_suffix $uri =404;
    }
}
kaffolder
  • 285
  • 1
  • 2
  • 7
  • What is not working? – Michael Hampton Sep 22 '14 at 03:27
  • @MichaelHampton Neither the hi-res or webp images are being served. If I reference a line such as `` in my HTML, then I should be served "assets/img/sample-photo@2x.webp on my Macbook Pro in Chrome. Currently, the hi-res cookie is being set (I've confirmed in the browser) and the webp_suffix conditional variable is getting set correctly. Something's not working correctly on the file output though (i.e. try_files) since I'm not seeing the correct files being served to the browser. – kaffolder Sep 22 '14 at 04:20

1 Answers1

2

In your try_files directive, you look up for image $uri$webp_suffix, which resolves to image.png.webp. I think you want to look up for $1$webp_suffix there.

Otherwise, I recommend you to enable nginx debug_connection <your_IP> directive in your main configuration, and you will get a detailed log of what happens during the request. There you can better see what is happening. If you cannot figure it out, please add the log for the request to the question and we can help better.

Tero Kilkanen
  • 34,499
  • 3
  • 38
  • 58
  • Thank you so much @tero-kilkanen ! That try_files line was the problem. Can't believe I missed that. :) – kaffolder Sep 22 '14 at 17:04