Nginx – How to set expires headers for images

Nginx - How to set expires headers for images

nginx (engine x)

In Nginx, you can easily set browser caching for your images. Nginx sets the ‘Expires’ and ‘Cache-Control’ http request headers for images nginx serves. This allows the client’s browser to cache the images for the amount of time specified by the expires tag inside the location block of code.

Here’s the location block I’m using in my Nginx virtual host configuration file:


location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires 1y;
log_not_found off;
}

You can use the curl command on unix/linux/mac to see the full headers for a request. Here is an example of the full headers nginx is setting for an image after using expires headers.


# curl -I http://nicholaskuechler.com/wp-content/uploads/2011/02/cropped-andromeda-galaxy11.jpg
HTTP/1.1 200 OK
Server: nginx/1.0.0
Date: Sun, 24 Apr 2011 04:26:46 GMT
Content-Type: image/jpeg
Content-Length: 66713
Last-Modified: Fri, 25 Feb 2011 08:34:30 GMT
Connection: keep-alive
Expires: Mon, 23 Apr 2012 04:26:46 GMT
Cache-Control: max-age=31536000
Accept-Ranges: bytes

I noticed after putting the nginx expires in place and utilizing client browser caching, my Google Page Speed score increased by 5 points. Excellent!

15 thoughts on “Nginx – How to set expires headers for images

  1. I have been trying to do this for my site, but I am not having much luck at all.

    I tried this and it disabled all images, css and js.

    location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
    expires 1y;
    log_not_found off;
    }

    Then I tried this (without *) and removed css and js and it removed all my images.

    location ~ \.(png|jpg|jpeg|gif|ico)$ {
    expires 1y;
    log_not_found off;
    }

    If my images are located… domain.com/assets/img/ & domain.com/images/ how can I get this to work? nginx is frustrating!

    Any help is much appreciated.

  2. I have been trying to do this for my site, but I am not having much luck at all.

    I tried this and it disabled all images, css and js.

    location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
    expires 1y;
    log_not_found off;
    }

    Then I tried this (without *) and removed css and js and it removed all my images.

    location ~ \.(png|jpg|jpeg|gif|ico)$ {
    expires 1y;
    log_not_found off;
    }

    If my images are located… domain.com/assets/img/ & domain.com/images/ how can I get this to work? nginx is frustrating!

    Any help is much appreciated.

    1. Hi Emma,

      Are you able to copy/paste your entire vhost so we can check it out? For my site, I have my vhost for nicholaskuechler.com that looks like this:

      server {
      listen 80;
      server_name nicholaskuechler.com

      # … some basic stuff for wordpress and other stuff specific to my server

      # inside of the server block, but not inside of any other blocks,.
      # I have my location block for expires:
      location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
      expires 1y;
      log_not_found off;
      }
      # Notes:
      # ~* means to match case-insensitive so it matches abc.jpg and abc.JPG
      # \.(js|css|etc)$ means it will match any file ending in .js, .css, .etc
      }

  3. This is how my configuration file looks:

    user nobody;
    # no need for more workers in the proxy mode
    worker_processes 2;
    error_log /var/log/nginx/error.log info;
    worker_rlimit_nofile 20480;
    events {
    worker_connections 5120; # increase for busier servers
    use epoll; # you should use epoll here for Linux kernels 2.6.x
    }
    http {
    charset utf-8;
    server_name_in_redirect off;
    server_names_hash_max_size 10240;
    server_names_hash_bucket_size 1024;
    include mime.types;
    default_type application/octet-stream;
    server_tokens off;
    disable_symlinks if_not_owner;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 5;
    gzip on;
    gzip_vary on;
    gzip_disable “MSIE [1-6]\.”;
    gzip_proxied any;
    gzip_http_version 1.1;
    gzip_min_length 1000;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    # You can remove image/png image/x-icon image/gif image/jpeg if you have slow CPU
    gzip_types text/plain text/xml text/css application/x-javascript application/xml image/png image/x-icon image/gif image/jpeg application/xml+rss text/javascript application/atom+xml;
    ignore_invalid_headers on;
    client_header_timeout 3m;
    client_body_timeout 3m;
    send_timeout 3m;
    reset_timedout_connection on;
    connection_pool_size 256;
    client_header_buffer_size 256k;
    large_client_header_buffers 4 256k;
    client_max_body_size 200M;
    client_body_buffer_size 128k;
    request_pool_size 32k;
    output_buffers 4 32k;
    postpone_output 1460;
    proxy_temp_path /tmp/nginx_proxy/;
    client_body_in_file_only on;
    log_format bytes_log “$msec $bytes_sent .”;
    include “/etc/nginx/vhosts/*”;
    }

    Where exactly would i put this code…sorry i’m a noob!

    Thanks.

  4. Our images are being cached but not the main index file of the site…or any .html pages for that matter.

    This is what we have under ghosts

    location ~* ^.+.(js|css|png|jpg|jpeg|gif|ico|htm|html)$
    {
    expires 30d;
    proxy_cache mydomain.com-static;

    I think our host added the htm and html values to try to get it to work but obviously it hasn’t. Please help.

    Thanks

    1. Hi Danny,

      I think this may work for you, by changing your current location block to the following:

      location ~* \.(js|css|png|jpg|jpeg|gif|ico|html|htm)$ {
      expires 30d;
      }

      That location block will go inside of the configuration file for your virtual host. So if your vhost configuration file is /etc/nginx/vhosts/danny.com then the location block would go inside of the danny.com file, and also inside of the server { } block. Something like this:

      server {
      listen 80;
      server_name danny.com

      # ... the rest of your virtual host configuration stuff goes here

      location ~* \.(js|css|png|jpg|jpeg|gif|ico|html|htm)$ {
      expires 30d;
      }
      }

      What’s the URL to your site? Also, you can check if the cache-control and expires are working using the curl command, like this:

      curl -I http://nicholaskuechler.com/wp-content/uploads/2011/04/nginx-logo.png

      If everything is set up correctly you would see this in the curl header output:

      Expires: Wed, 19 Jun 2013 16:55:02 GMT
      Cache-Control: max-age=31536000

      Cheers

  5. To Emma: I hope it’s not too late for a bit of help… You don’t have the root in this [location] and nginx will search for all these images in the “global root” (either the [http] or the [server] root) i.e. /completely/different/path/
    Try this:

    location ~* \.(jpg|jpeg|gif|png|css|js|ico|xml)$ {
    root /path/to/root/;
    access_log off;
    log_not_found off;
    expires 180d;
    }

    Cheers! ;)

  6. Hi I am struggling to understand exactly how the headers work.

    I would like to know what the difference between

    no-cache
    Private

    I have a site similar to mine that has private for the cache setting, but on my site it says no-cache ?

    Thanks
    Greg

  7. Hi Nicholas,

    In nginx I find that using the “expires” directive also adds the “Cache-control” header. Is there a way to set expires alone with Cache-control ?

    1. Hey Jesin,

      Looking over the nginx docs, I’m not sure it’s possible to add expires without the Cache-control header.

      I’m curious though: what’s your use case for this?

      Cheers,
      Nick

    2. Hey Jesin,

      You can also set specific Cache-control headers in nginx with the add_header option, such as:

      expires 30d;
      add_header Cache-Control private;

      Then you would be able to set Cache-Control as you need.

      Cheers,
      Nick

  8. Hi,

    I tried this but now by css doesn’t load, here is part of my conf

    location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
    root /webapp/myapp/static/;
    access_log off;
    expires 1y;
    log_not_found off;
    }

    1. Hey Przemek,

      It is hard to say exactly what’s going on without seeing more of your nginx config for your site. I wonder if it could be a configuration ordering issue?

      Here is an alternate solution that may work out for you. It’s something I’m doing with a python Django application. I have some static assets being served from my URL, such as mywebsite.com/static/some_static_asset_here.css

      location /static {
      alias /home/some_username/some_app/some_path/static/;
      expires 30d;
      }

      I hope that helps!

      Cheers,
      Nick

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>