Conditionally Serve Webp, JXR Images with Nginx

This post is over six (6) months old. Some things on this page my be out of date or no longer applicable.


Throughout this site I'm using Webp Detect to serve webp images to browsers who advertise that they accept that type of image in their accept header. Many thanks go too Eugene Lazutkin (@uhop) and Ilya Grigorik (@igrigorik) for fleshing this all out.

Microsoft Edge now advertises that they accept "image/jxr" in their accept header. The Image below should read "Webp" on Chrome/Blink based browsers, "JPG" for Firefox Old IE and old Opera, and "JXR" for Microsoft Edge - at least as of Edge 20.10240.16384.0 .

pineapple in field

Image has been scaled down to 800x533 and the JPG image optimized through MozJpeg .


Nginx How To

Getting the ability to serve JXR files to Microsoft Edge calls for at the least two changes to the solution linked above by Eugene Lazutkin and Ilya Grigorik. If your mime.types already has the correct mime type for jxr images you're already half way there.

First step is to add in the proper mime type for jxr images inside Nginx's mime.types

 image/vnd.ms-photo  jxr wdp hdp;
# image/jxr  jxr wdp hdp; will break the negotiation in Edge 20.10240.16384.0

Follow this up by changing $webp_suffix in the $http_accept map directive to $img_suffix and adding in the map parameters for jxr.

map $http_accept $img_suffix {
  "~*webp"  ".webp";
  "~*jxr"   ".jxr";
}

The last change needed is in the location block you are serving your images from in the "try_files".

# example location block only dealing with images with these extensions
  location ~* \.(?:png|jpe?g|gif|webp|jxr)$ {
    expires 1y;
    add_header Vary $vary_header;
    add_header Cache-Control $cache_control;
    # now serve our images
    try_files $uri$img_suffix $uri =404;
  }

Apache How To

If you'd like to do this with Apache have a look a Webp Detect - Apache . It doesn't have jxr built in with it and I'm not very good with Apache rewrites but it can set you up with at least webp!

Creating JXR

Creating JXR images can be an absolute pain. So if you're not into installing source and building a tool I suggest using Microsoft's JXR PhotoShop Plugin or GIMP's JPEG XR plugin . The command line tool gives you better compression and file size but it suuuuuuuuuuuuuuucks to use. There's very little documentation, ImageMagick's jxr conversion seems broken each time I try to use it too.

For the command line tool on Ubuntu install libjxr-tools. It should install all the dependencies then you can go about creating a jxr image with a slight caveat. JxrEncApp only takes three types of images as input.

  1. bmp
  2. tif
  3. hdr

Like I said. A GIANT PAIN. So in your image editor of choice save an image or convert it too tiff then run a command similar to the one below.

JxrEncApp -i input-file.tif -o output-file.jpg.jxr -q 0.6

The -q parameter is for quality. Ranges are from 0.0 - 1.0 with the default of 1.0 being lossless. Typing JxrEncApp into your terminal will give you all the options. Good Luck haha.

Creating Webp

Creating Webp Images on the other other hand is fairly straight forward with a few tools to actually use.

I created the large webp image as follows

cwebp -q 70 pineapples-800-to-webp.jpg -o pineapples-800.jpg.webp

Converting a gif to animated webp

gif2webp 15simplependulum.gif  -o 15simplependulum.gif.webp

Converting Simple Pendulum by Lookang which is 707KB to an animated webp returns a 354kb animated webp.

File Sizes

File Size
JPG 52kb
Jpeg XR (jxr) 53kb (0.5 Quality)
Webp 33kb (50 Quality)

Note on File Sizes

Other Images compress better with JPEG XR its really trial and error. For Instance the Lena Reference Image using ImageMagick to convert from tiff to jpg we get a JPG image that is 42KB.

convert -quality 80 lena512color.tiff lena.jpg

The 80 quality JPG compressed with MozJPEG through NPM will give us a 26kb file.

mozjpeg -outfile lena-jpg-moz.jpg -quality 70 lena.jpg

Using JxrEncApp to convert the tiff to jxr we will get a JXR that is 25KB

 JxrEncApp -i lena512color.tiff -o lena-tiff-to-jxr.jxr -q 0.4 

Using cwebp to convert the 80 quality JPG to webp at a quality of 60 will give us a 18kb image.

cwebp -q 60 lena.jpg -o lena-60.webp

Serving these browser specific image formats can give us big gains (specifically webp in most instances) compared to a non-optimized image format. JXR images can/do offer good compression for some images but MozJpeg gives us that, offers tooling for multiple OS's and can be used for anything that accepts a JPG image (read every browser) unlike JXR.

You can find the images I mentioned above, the Lena reference image (and links to mozjpeg tools) in my github linked below with the nginx.conf.

Lena Image Type Size
jpg 42KB (80 Quality)
MozJpeg 26kb (70 Quality)
Jpeg XR (jxr) 25KB (0.4 Quality)
Webp 18kb (60 Quality)

Nginx.conf File

You can find an example of the nginx.conf file and an updated mime.types with jxr mime types at my Github page .

Also any questions just file an issue and I'll try to help as best I can. Or head over to webp-detects github page linked above and ask there. They are the ones who got this whole ball rolling!

Additions:

  1. Added Jpeg xr conversion tools and how too
  2. Added Webp tools and docs
  3. Added File Sizes