Search code examples
nginxnginx-reverse-proxycache-controlnginx-config

how to add cache control header with proxy pass in nginx for some file extensions


I like add cache control header with nginx for some extensions such as .jpg, etc but so far some of the solutions I found on the net, I couldn't get it to work. I will tell you what I have tried.

I have tried variations of the following in different place in the .conf file of my site and when I tried the site become blank and I found a lot of 404 errors on the console. The site is developed in React.

location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
    expires 1d;
    add_header Cache-Control "public, no-transform";
}

My conf file looks like the following. The thing is I have to do reverse proxy as the sites are actually hosted in Docker containers.

 server {
    server_name mysite.net;
    root         /usr/share/nginx/html;

    location / {
        proxy_pass http://web:3005/;
    }

    location /api/ {
        rewrite ^/api(/.*)$ $1 break;

        proxy_pass  http://api:5005/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_redirect    off;
        fastcgi_read_timeout 1200;
        proxy_read_timeout 1200;
    }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/mysite.net-0001/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/mysite.net-0001/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
    if ($host = mysite.net) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    server_name mysite.net;
    return 404; # managed by Certbot
}

Solution

  • Use the map directive:

    map $cache $control {
        1       "public, no-transform";
    }
    
    map $cache $expires {
        1       1d;
        default off; # or some other default value
    }
    
    map $uri $cache {
        ~*\.(js|css|png|jpe?g|gif|ico)$    1;
    }
    
    server {
        ...
        expires $expires;
        add_header Cache-Control $control;
        ...
    }
    

    (you can also put expires and add_header directives into the location context or even leave then in the http context). nginx won't add the header at all (or modify an existing header) if the value calculated via the map expression for $control variable will be an empty string. This is not the only possible solution, you can also rely on the Content-Type response header from your upstream (see this answer for an example).

    You should be aware of this documentation excerpt:

    There could be several add_header directives. These directives are inherited from the previous configuration level if and only if there are no add_header directives defined on the current level.