I'm having issues with the expires header field. The following nginx rules give me this header (no expire header). Ideas why the expires headers arn't being passed?

Header for: /css/v1/afile.css

HTTP/1.1 200 OK
Server: nginx
Date: Sat, 14 Sep 2013 07:29:59 GMT
Content-Type: text/css
Content-Length: 12548
Last-Modified: Sat, 11 May 2013 11:05:51 GMT
Connection: keep-alive
Accept-Ranges: bytes

Nginx config:

server {
    listen 80 default_server;
    server_name _;
    root    /var/www/apps/myapp/public/app/webroot;
    index   index.php index.html index.htm;

    server_tokens off;

    access_log  /var/www/apps/myapp/logs/access.log;
    error_log   /var/www/apps/myapp/logs/error.log;

    client_max_body_size 20M;

    rewrite_log on;

    location / {
        try_files $uri $uri/ /index.php?$args;

    location ~ /(js|css)/v[0-9]+/(.*) {
        access_log off;
        expires 7d;
        add_header Cache-Control public;
        try_files $uri $uri/ /$1/$2;

    # Pass the PHP scripts to FastCGI server
    location ~ \.php$ {
        fastcgi_pass   backend;
        fastcgi_index  index.php;
        fastcgi_intercept_errors on; # to support 404s for PHP files not found
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include        fastcgi_params;

The following should work to display the expires header that was set in the same location block...

try_files $uri $uri/ /$1/$2 =404;

As per Nginx wiki, try_files should end with a URI or with a status code. You may get more information on why expires aren't set, if you enable debugging in Nginx.


Ideas why the expires headers arn't being passed?

I enabled debugging to figure out this interesting situation. This is what I found...

Here's a direct quote from the try_files wiki article linked earlier...

If none of the files were found, an internal redirect to the uri specified in the last parameter is made.

So, when you used the following code...

try_files $uri $uri/ /$1/$2;

the nginx could not find $uri and $uri/ (in your case /css/v1/afile.css and /css/v1/afile.css/), an internal redirect to the URI specified in the last parameter is made. So, after the internal redirect, the location /$1/$2 is found (in your case /css/afile.css), in another location block. Since, it is executed by another location block that doesn't have any expires, you didn't see expires.

  • Wow — Amazing! Extremely insightful! Thanks for looking deeply into this matter. Now I understand why I saw lots of try_files rule end with =404; – iDev247 Sep 15 '13 at 6:23
  • It's my pleasure to have learned something from this issue. – Pothi Kalimuthu Sep 15 '13 at 6:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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