112

I'm using nginx to server my static content, is there a way that I can set the expires headers for every file that meets a specific rule? For example can I set the expires header for all files that have an extension of '.css'?

Jesse Nickles
  • 264
  • 2
  • 14
Unkwntech
  • 1,760
  • 4
  • 19
  • 24

8 Answers8

141

I prefer to do a more complete cache header, in addition to some more file extensions. The '?' prefix is a 'non-capturing' mark, nginx won't create a $1. It helps to reduce unnecessary load.

location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
    expires 30d;
    add_header Pragma public;
    add_header Cache-Control "public";
}
J. M. Becker
  • 2,471
  • 1
  • 17
  • 21
  • 9
    All my static files were not found after adding that. – Jürgen Paul May 31 '12 at 00:45
  • @JackSpairow: I can't really explain why that happened, as It has always worked for me. Are you running Nginx missing the add_header providing module? This kind of thing really is limited in scope, you sure another deceleration is not a problem in combination? – J. M. Becker May 31 '12 at 17:26
  • 29
    Probably another block had definition for the static files with a `root` set, in that case you should add the directives to that block. (I know this is 2y late, but for future citizens) – aularon Jul 05 '14 at 23:36
  • 1
    I personally appreciate clarifications, especially for future searchers because they often show up long after the original post. +1 :P – J. M. Becker Jul 21 '14 at 18:14
  • using this totally messes up my wordpress website. css and images are not show. is there any other conflict somewhere? – user1641443 Apr 27 '15 at 10:57
  • Remember nginx matches by location only, so this is an extremely common problem. Also file extension matches are always a source of problems, especially with PHP. first make sure you have a root set, either globally or if not, set one in the location bracket. – J. M. Becker Apr 27 '15 at 21:04
  • how to apply it to proxified backend. which is served by: proxy_pass http://app_servers;? – aholbreich Dec 27 '15 at 11:52
  • This answer is wrong, and will lead to problems. Check @amurell answer below https://serverfault.com/a/571403 – Kuzeko Sep 27 '17 at 08:43
  • @Kuzeko, It's not wrong, it simply doesn't consider what the user's configurations already are, as a potential problem can lead far outside the question's answer. The real answer is simpler, don't apply configuration snippets without really knowing the rest of your configurations. At bare minimum, your doing a trial and error, so expect possible error. – J. M. Becker Oct 21 '17 at 15:31
  • please add `root` directive here to make this answer comprehensive – Shirker Feb 06 '18 at 09:06
23
server {
    ...

    location ~* \.css$ {
       expires 30d;
    }
    ...
}

The location directive

The expires directive

Dave Cheney
  • 18,567
  • 8
  • 49
  • 56
22

I don't have enough reputation to comment on why the accepted answer would cause the files to no longer show up, but I figured it out and would like to help out!

Short version:

Make sure you have a root directory specified for your location block on images if you do not have a global one set!

Long version below:


First of all, my method of implementing this solution was really similar to this answer, where you write the rule (as in the accepted answer):

location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
    expires 30d;
    add_header Pragma public;
    add_header Cache-Control "public";
}

into a file img-cache.conf

and then include that file into your server {...} directive.

My example of somesite.com in my sites-available folder:

 #Image Caching
 include /etc/nginx/conf/img-cache.conf;

That way you can add the image caching location block to multiple sites you might be running.


Second of all, I have a situation where my /var/www/ contains two folders that I allow as public_html - secure and training, so I have to make specific location blocks in my site's server directive singling out these folders.

As such, I do not have a global root directory set.

So when you make your image location blocks, you may not be providing them with a root directory from which to look for the images in!

My solution was then to:

location ~ ^/training.+\.(?:ico|css|js|gif|jpe?g|png)$ {
        root /var/www/;
        expires 7d;
        add_header Pragma public;
        add_header Cache-Control "public";
        try_files $uri 404;
}

location ~ ^/.+\.(?:ico|css|js|gif|jpe?g|png)$ {
        root /var/www/secure;
        expires 7d;
        add_header Pragma public;
        add_header Cache-Control "public";
        try_files $uri 404;
}
amurrell
  • 591
  • 1
  • 7
  • 13
  • 2
    +1 for making this a reusable `.conf`. The appropriate folder in `nginx/1.14.0 (Ubuntu)` seems to be `/etc/nginx/snippets/`. – Jan Werkhoven Oct 26 '18 at 09:57
9

You can also set the expires to maximum. Here is the directive I use for css and js.

# Set css and js to expire in a very long time
location ~* ^.+\.(css|js)$ {
    access_log off;
    expires max;
}
Jauder Ho
  • 5,507
  • 2
  • 19
  • 17
  • 1
    I would use the root directive only in the server {} block, when using it in sub locations it leads to unexpected consequences. You don't need the break; either, as you're not in an if {} block – Dave Cheney Jun 10 '09 at 10:47
  • You are right. Forgot to clean this up. Edited to reflect this. – Jauder Ho Jun 11 '09 at 01:02
9

All the aforementioned solutions will deny the possibility to have different aliases for different paths. Also for the sake of having all your different cache expirations in one place you should use nginx map in the following way.

...

# Expires mappings
map $sent_http_content_type $expires {
    default                    off;
    text/html                  epoch;
    text/css                   max;
    application/javascript     7d;
    ~image/                    max;
}

...

server {
   listen ...;

   expires $expires;
   location /assets/ {
      # It is now possible to serve some resources from different path
      alias /var/www/my_other_path/
   }

   # and have them all have same expirations
   location / {
       try_files $uri $uri/ /index.html;
   }
...
}

Off disables caching, epoch (for unix epoch) results in resource always being refetched, max sets the date to browser max value.

The ~image/ matches any image types.

More about nginx maps at http://nginx.org/en/docs/http/ngx_http_map_module.html.

Pantura
  • 91
  • 1
  • 1
2

If you have one place that is home to all your static files, something like this will do...

 location /static {
            your/location/to/static/files/static;
            expires 30d;
            add_header Cache-Control "public";
    }

The accepted answer caused nginx to not find any of my static files. Not really sure why, but this is a simple alternative.

user161646
  • 31
  • 1
  • I voted for this one but make sure you add the `/static` folder (whatever you set in location) at the end of the alias (right after `.../files` in the example). – Miro J. Jun 07 '19 at 15:16
0

Most of the answers on this page are extremely outdated and/or convoluted...

For starters, the Expires header is not a good option these days, nor is the Pragma header... ideally, you should disable both of these headers in your Nginx configuration.

more_clear_headers "Pragma Expires";

It's much cleaner to set a single Cache-Control header on your static assets location block:

location ~* \.(atom|bmp|bz2|css|doc|docx|eot|gif|gz|ico|jpeg|jpg|js|mid|midi|mp4|ogg|ogv|otf|png|ppt|rar|rss|rtf|svg|svgz|tar|tgz|ttc|ttf|wav|webp|woff|woff2|xls|zip)$ {
    add_header Cache-Control "public, max-age=691200";
}

Remember that for Nginx, if you use the expires directive such as expires max this is NOT just setting an HTTP Expires header, it's also setting a Cache-Control header too, so it can get very messy rather quickly.

TLDR just use Cache-Control on your static files so that CDNs like Cloudflare can cache them at the edge and/or browsers can cache them for a while.

And I recommend NOT using any cache headers for your page/HTML content on dynamic sites like WordPress because it will only lead to conflicts and confusion... if you really want to, you can just use an etag for your pages, but I personally don't and think those make more sense when using aggressive output caches like Varnish or Litespeed's LS Cache.

The approaches above are what we use in SlickStack and it works fantastic for performance and stability, while guarding against e.g. WordPress plugins (or people) messing with your settings.

Jesse Nickles
  • 264
  • 2
  • 14
-1

Step1: Configuring Cache-Control and Expires Headers:

sudo nano /etc/nginx/sites-available/default

Add the following above the server block:

# Expires map
map $sent_http_content_type $expires {
    default                    off;
    text/html                  epoch;
    text/css                   max;
    application/javascript     max;
    ~image/                    max;
}

And this line within server block. expires $expires;

sudo systemctl restart nginx

Source: DigitalOcean