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!
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;
}
}