In Nginx how to assign cache control response header - nginx

Based on the response code I would like to set the cache control header in map if it is 301 the set "no-cache, no-store, must-revalidate" else default set the value which came with the response. Is there a response_cache_controle or similar command
map $status $bb2_cache_control_header {
301 "no-cache, no-store, must-revalidate";
default $response_cache_control";
}

Use add_header directive as follow:
add_header Cache-Control $bb2_cache_control_header;

Related

Nginx: How to set headers for all the files under a specific folder

So Let's say I have my application where files are under
https://myapp.com/v1/assets/images/*.jpg
and
https://myapp.com/v1/assets/js/*.jpg
I would like to add a rule that would set no-cache headers to anything under /assets
I believe what I can for now do is
location ~ .*assets/js/.*$ {
add_header Cache-Control "public, max-age=0, no-store";
}
location ~ .*assets/images/.*$ {
add_header Cache-Control "public, max-age=0, no-store";
}
But that seems not working plus if I have many other folders under assets, I will need to add a separate rule.
Can I group everything in a pattern so that anything under /assets/* would have that header?
Thanks
This can be done via map directive:
map $uri $cache_control {
~/assets/(images|js)/ "no-cache, no-store, must-revalidate";
}
server {
...
add_header Cache-Control $cache_control;
...
}
If your URI won't match the regex, $cache_control variable would have an empty value and nginx won't add that header to its response. However there are other nginx directives that could affect Cache-Control header, i.e. expires. If you have something like expires <value>; in your config, you can use two map blocks:
map $uri $cache_control {
~/assets/(images|js)/ "no-cache, no-store, must-revalidate";
}
map $uri $expire {
~/assets/(images|js)/ off;
default <value>;
}
server {
...
expires $expire;
add_header Cache-Control $cache_control;
...
}
And take a look at this answer to not be surprised with add_header directive behavior.

Disabling Cache-Control in Nginx for certain IPs

Static files on my server served with browser caching via Cache-Control headers:
location ~* \.(css|js|gif|jpe?g|png)$ {
expires 1h;
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}
Is it possible to disable this header for certain IPs so they would not cache any files?
P.S. I need it for users who log in as administrators to see the last changes.
The expires directive can be controlled by a variable, usually generated by a map directive. See this document for details.
For example:
map $remote_addr $expires {
default 1h;
10.1.2.3 -1;
}
server {
...
location ~* \.(css|js|gif|jpe?g|png)$ {
expires $expires;
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}
}
It is assumed that the add_header statements can remain, even if expires is set to -1 to disable the caching.

Define specific cache control header for selected file only

I'm setting up a Nginx sever (version 1.17.1) for Gatsby following up the recommendation at https://www.gatsbyjs.org/docs/caching/.
The snippet below is the portion my server {} block attempting implementing the recommended caching configuration;
location ~* \.(?:html)$ {
add_header Cache-Control "public, max-age=0, must-revalidate";
}
location /static {
add_header Cache-Control "public, max-age=31536000, immutable";
}
location ~* \.(?:css|js)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
location /sw\.js {
add_header Cache-Control "public, max-age=0, must-revalidate";
}
Equally tried an if statement in place of the location {} block for defining cache configuration for the service worker file, sw.js, as below;
if ($request_uri ~ ^sw\.(?:js)$) {
set $no_cache 1;
}
Unfortunately, all files get cached successfully as expected except sw.js.
What am I doing wrong and how can I fix it so as to effectively set cache control header for sw.js to public, max-age=0, must-revalidate?
I ended up with the following nginx configuration regarding caching for Gatsby.js:
location ~* \.(?:html)$ {
add_header Cache-Control "public, max-age=0, must-revalidate";
}
location /page-data {
add_header Cache-Control "public, max-age=0, must-revalidate";
}
location = /sw.js {
add_header Cache-Control "public, max-age=0, must-revalidate";
}
location /static {
add_header Cache-Control "public, max-age=31536000, immutable";
}
location ~* \.(?:js|css)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
#OP: With which configuration did you end up? Maybe you can edit this answer to match the perfect solution after all; for people searching for "caching nginx gatsby".
The order of precedence of location is described here https://nginx.org/en/docs/http/ngx_http_core_module.html#location
When an exact match is found (using the = modifier) the search terminates and regular expressions will not be checked, so you can use that for your sw.js:
location = /sw.js {
add_header Cache-Control "public, max-age=0, must-revalidate";
}

Using a Header to Filter Proxied Response Headers

I have an upstream server that often sets Cookie(s) by returning the "Set-Cookie" response header.
I would like to have an nginx proxy in front of said upstream server:
Browser => Nginx => Upstream
If the Browser => Nginx request had the header X-No-Cookies: true I'd like the response from Upstream => Nginx => Browser not to contain the Set-Cookie response header. If X-No-Cookies had any other value, I'd lie the Set-Cookie response header to be returned unaltered. I'm not able to change the response header behavior of the upstream server.
Currently my nginx config is as follows, pay specific attention to the use of the proxy_hide_header directive. I've also echoed the $proxy_hide_header variable in the X-No-Cookies response header.
map $http_x_no_cookies $proxy_hide_header {
default "";
"true" "Set-Cookie";
}
# Homepage
server {
listen 80;
server_name example.com;
location /api {
proxy_pass https://example.com/api;
proxy_hide_header $proxy_hide_header;
add_header "X-No-Cookies" $proxy_hide_header;
}
}
When I make a request with cURL:
curl \
http://example.com/api \
-H 'X-No-Cookies: true' \
-I
I get the following response headers:
Server: nginx/1.12.2
Date: Thu, 13 Dec 2018 02:26:41 GMT
Content-Type: application/json
Content-Length: 2255
Connection: keep-alive
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Accept,Authorization
Access-Control-Allow-Methods: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS
Access-Control-Expose-Headers: Content-Length
Set-Cookie: foo=bar; Max-Age=2592000; Expires=Sat, 12 Jan 2019 02:26:41 GMT; Path=/; Domain=example.com; Secure
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-No-Cookies: Set-Cookie
Whenever the proxy_hide_header is provided an nginx variable as an argument it seems to have no effect. If I swap the variable for a string ($proxy_hide_header substituted for "Set-Cookie") I get the desired behaviour - the Set-Cookie response header is omitted.
Edit: I've pushed the code for this question to GitHub
My original (non-working) implementation is here
Ivan Shatsky's solution is here
What an interesting challenge! Truly, $proxy_hide_header does not accept variables as its parameter and cannot be used inside an if blocks. Also we cannot use $upstream_... variables directly inside a location block because its values are not evaluated yet. Finally I found a solution. We always hide Set-Cookie header and then set it again if needed, values calculating through map expressions:
map $http_x_no_cookies $hide_cookies {
default "0";
"true" "1";
}
map $hide_cookies$upstream_http_set_cookie $cookies {
~^0(.*)$ $1;
}
upstream backend {
server example.com;
}
server {
listen 80;
server_name example.com;
location /api {
proxy_pass https://backend/api;
proxy_hide_header Set-Cookie;
add_header Set-Cookie $cookies;
}
}

NGINX Reverse Proxy and Access-Control-Allow-Origin issue

I'm configuring an NGINX Reverse Proxy.
On the browser I go to:
client url: https://www.hollywood.com
Then the web page above needs to do requests to:
server url: https://server.hollywood.com/api/auth/login
This is the configuration corresponding to: server.hollywood.com:
server {
listen 443 ssl;
server_name server.hollywood.com;
# add_header 'Access-Control-Allow-Origin' "https://www.hollywood.com" always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With' always;
ssl_certificate ../ssl/lets-encrypt/hollywood.com.crt;
ssl_certificate_key ../ssl/lets-encrypt/hollywood.com.key;
location /
{
proxy_pass http://localhost:9201;
include "../proxy_params.conf";
}
}
Experiment 1:
With the Access-Control-Allow-Origin line commented out, when I access to:
client url: https://www.hollywood.com
I get the following error on the browser console (Chrome in my case):
POST https://server.hollywood.com/api/auth/login/local 502 (Bad Gateway)
(index):1 Failed to load https://server.hollywood.com/api/auth/login/local: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://www.hollywood.com' is therefore not allowed access. The response had HTTP status code 502.
Experiment 2:
If I enable the Access-Control-Allow-Origin line above, then I get on the browser terminal:
Failed to load https://server.hollywood.com/api/auth/login/local: Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header contains multiple values '*, https://www.hollywood.com', but only one is allowed. Origin 'https://www.hollywood.com' is therefore not allowed access.
I don't know why multiple when before that header was not present???
Experiment 3:
In the other hand, if I go directly on the browser to the:
server url: https://server.hollywood.com/api/auth/login
with the Access-Control-Allow-Origin line commented out, I get the following (on the Network section):
Response Headers:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 139
Content-Security-Policy: default-src 'self'
Content-Type: text/html; charset=utf-8
Date: Sat, 09 Jun 2018 06:34:00 GMT
Server: nginx/1.13.12
X-Content-Type-Options: nosniff
Before I got: "No 'Access-Control-Allow-Origin' header is present on the requested resource." but now I see above that field is in there on the Response Headers.
Experiment 4:
If I enable again the Access-Control-Allow-Origin line above, then I get the following (on the Network section):
Response Headers:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Origin: https://www.hollywood.com
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 139
Content-Security-Policy: default-src 'self'
Content-Type: text/html; charset=utf-8
Date: Sat, 09 Jun 2018 06:34:58 GMT
Server: nginx/1.13.12
X-Content-Type-Options: nosniff
Now I get two times the field: Access-Control-Allow-Origin.
Do you have any idea why my first 2 experiments are failing getting the errors relative to: Access-Control-Allow-Origin?
Thanks!
It could be that the server behind your proxy_pass was setting the Access-Control-Allow-Origin header as well.
For what it's worth for future readers with a similar problem, I found that my node.js server was passing an Access-Control-Allow-Origin: '*' header for some reason, as well as the actual header I'd set in node.js to restrict CORS. When commenting out my node.js cors middleware, the Access-Control-Allow-Origin: '*' header still remained.
To resolve this, I used the nginx proxy_hide_header directive to remove the header coming from node.js and manually adding it as it should be:
# proxying the
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# local node.js server
upstream websocket {
server 127.0.0.1:3000;
}
server {
server_name ...;
# ...;
# add the header here
add_header Access-Control-Allow-Origin https://www.hollywood.com;
# Websockets config
location /socket.io/ {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# do not pass the CORS header from the response of the proxied server to the
# client
proxy_hide_header 'Access-Control-Allow-Origin';
proxy_pass http://websocket;
proxy_http_version 1.1;
}
location / {
# ...
try_files $uri /index.html;
}
}
Googling this issue was pretty hard since most people are trying to fix CORS by making the Access-Control wide open! Here was another issue with similar problems:
https://serverfault.com/questions/751678/how-can-i-replace-access-control-allow-origin-header-in-proxy-response-with-ngin
Try this configuration:
server {
listen 443 ssl;
server_name server.hollywood.com;
ssl_certificate ../ssl/lets-encrypt/hollywood.com.crt;
ssl_certificate_key ../ssl/lets-encrypt/hollywood.com.key;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
proxy_pass http://localhost:9201;
include "../proxy_params.conf";
}
if ($request_method = 'GET') {
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
proxy_pass http://localhost:9201;
include "../proxy_params.conf";
}
}
}
Code based on https://enable-cors.org/server_nginx.html
Hope it helps.

Resources