Ever notice that image galleries on WordPress.com load faster than those on your own? WordPress.com can serve dynamically-resized images on-the-fly to improve page load speeds and create pretty-looking image galleries. You can achieve this on your own server too, with a few simple tricks in your nginx.conf, taking advantage of URL parameters WordPress appends to image uploads and the nginx libgd-based ngx_http_image_filter_module.
WordPress’ dynamic image sizes serve two main purposes: aside from resizing the source images to a smaller size, it also lets you crop rectangular images to a thumbnail without completely skewing the aspect ratio and butchering the result.
Let’s take an example WordPress gallery, created using the JetPack galleries plugin. We’re taking a mix of square (logos) and rectangular (photos) media, and framing them in circles via CSS. On a properly-configured web server, it should show up like this:
But without dynamic image resizing, the original aspect ratios can be completely skewed, and you’ll get something like this instead:
Let’s take a look and see what is happening. When WordPress generates the blog post, the images are being created with query string arguments appended… “What’s that,” you say? “Query string arguments for static images?!” Indeed:
As you can see, WordPress is requesting a cropped version of the originally-uploaded file. On a normally-configured webserver, query arguments are ignored for attachments like this. But on WordPress.com’s servers, they’re interpreted by an image processor, and resized before being served. We can see proof of this by checking the headers for a request to a resized image on a WordPress.com blog:
There’s a lot going on, but just focus on the X-Orig-Src: 0_imageresize bit to validate our assumption.
With nginx’s ngx_http_image_filter_module, you can make nginx serve up resized images too. First things first, you’ll need to make sure that nginx is compiled with this module (it’s not by default). If it’s not you’ll need to recompile nginx from source, and specify –with-http_image_filter_module:
Then we need to configure nginx.conf to tell it what parameters to use for the width, height, and crop arguments. It’s a little tricky since WordPress can issue either ?w or ?h or both, with or without ?crop=1. Also, nginx can’t resize images inside an if block, so we’re going to need to use a specific location (and a rewrite within an if) to get around that limitation:
As you can see, we default $width and $height to – (which nginx specifies as the required value for unused parameters, otherwise you’ll get a HTTP 415 Unsupported Media Type error), then overwrite one or both depending on whether ?w= or ?h= were specified. This detection is outside the location blocks, and then we have a location block for our images and a fake location block (marked as internal for security/sanity reasons) for our cropped content that will serve the same content, only use the crop instead of resize argument to image_filter.