Server Fault is a question and answer site for system and network administrators. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have a PHP script that generates a thumbnail based on an existing path (more info). The basic idea is this:

Given the document root /web/, an image lives in /web/images/foo.jpg. When a predefined thumbnail filter named thumb is requested in the url /web/thumb/foo.jpg, the PHP script picks it up, and generates a thumbnail on that location, so that it will be served directly via Nginx on the next request.

Now this works with the following (simplified) configuration:

server {
  location / {
    try_files $uri @rewriteapp;

Where @rewriteapp contains the rewrite rule for the application.

The problem is that I want to add an Expires header to the cached thumbnails. But when I add something like this

location ~* \.(jpe?g|gif|png)$ {
  expires 1y;

It only works with generated thumbnails, but it returns a 404 on the request that should trigger the @rewriteapp rule.

I tried adding that last block before or after the first location block, I also tried to include the same try_files statement inside my location w/ expires block. But none of this works.

How can I add the header to my images?


The accepted answer below contains a more detailed explanation why this failed. In my case I solved it by replacing the second location block with this:

location /thumb/ {
  try_files $uri @rewriteapp;
  expires 1y; access_log off; log_not_found off;
share|improve this question
Sorry, lost track of what you're trying to do. Are you trying to add the expire header to the dynamically generated images or the static images served by Nginx? – Jason Ilicic Jan 29 '14 at 1:32
To the static images. – Peter Kruithof Jan 29 '14 at 12:42
up vote 0 down vote accepted

I have done pretty much exactly what you have done before, i'll copy my nginx vhost code below

basically ordering of your location blocks is somewhat important in this case as per:

anyway here is my nginx code - you can likely amend this to your local environment:

location ~ ^/thumbnails/(.*)/(.*)$ {
    try_files /thumbs/$1/cache/$2.jpeg /thumbs/$1/phpThumb.php?src=../../images/product/$2;
    expires      6w;
    fastcgi_hide_header Set-Cookie;

location ~* \.(ico|pdf|flv|jpg|jpeg|png|gif|js|swf|css|js)$ {
    expires      6w;

key things to note: the thumbnails dir doesnt exist on disk (its purely used for the purpose of try_files

this code basically means that for an incoming request say /thumbnails/small/13909967441.JPG

it will try to look for the file in /thumbs/small/cache/13909967441.JPG.jpg (yes i know dual extension - it was a hack for performance what more can i say!) if it finds it, then it serves it with the expires header, if it doesnt find it it then rewrites the request internally to the thumbnailing script configured for that size which generates the image and serves it, the expires header is also added to requests that go through the internal rewrite to the PHP request

the trick is specifically this line from the documentation:

If the longest matching prefix location has the “^~” modifier then regular expressions are not checked.

What that means is, by putting your rewrite/location above your regex rules like the generic one for image files and making sure the flags and having the ^~ or in my case ~ ^ (they both equate to same rule) in it prevents the regex rules from matching later on Tthis is because regex rules will normally override non regex matches if its not a = or ^~ match

requests that hit the /thumbnails path will never match the generic regex match for image files because of the above mentioned location that blocks regex matching and the fact that the regex block is after the location block so cant match before it.

Update - Based on your nginx code

you would add an expires config line into your location / block as this will set the headers for both the static images AND images coming from the rewrite block, obviously you would either have to remove the images regex location block or add the ^~ operator to your primary location block

share|improve this answer
Thank you for pointing me in the right direction! I should've read the location documentation more thoroughly. My case is a little different than yours, but the prefix matching vs regex matching part was the key to the answer. – Peter Kruithof Jan 30 '14 at 22:10

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.