Nginx – Alter Reverse Proxy Cache Header from Public to Private

cacheheadershttp-headersnginxreverse-proxy

I have an Nginx cache server getting content from an origin server, the origin server sets Cache-Control to Public so my Nginx cache server can cache the content and make less requests to origin. But when serving to users, I don't want it to send the cache as Public anymore, but set it to Private so others (outside of this network) cannot cache it or modify it.

I still need it to pass a max-age and tried to do it by a custom header sent, but the if condition fails.

this alone works OK:

add_header Cache-Control "private, max-age=$upstream_http_adr_private_cache_seconds";

this condition fails

if ($upstream_http_adr_private_cache_seconds) {

    add_header Cache-Control "private, max-age=$upstream_http_adr_private_cache_seconds";

}

But this test also fails:

if ($upstream_http_adr_private_cache_seconds) {

    return 404;

}

What am I doing wrong ? Or is there another way to alter the public to private ?

Best Answer

Directives from ngx_http_rewrite_module are processed before evaluation of $upstream_... variables. You should't treat nginx config as programming language where your operations executed sequentaly as they appears in the source code. For example, there is no matter if you use proxy_set_header directive before the proxy_pass one or after it. Directives from ngx_http_rewrite_module is a special story, they are different from any other directives, you can read some details here.

For your case you can use map translation:

map $upstream_http_adr_private_cache_seconds $cache_control {
    ""       ""; # empty string if no custom header present
    default  "private, max-age=$upstream_http_adr_private_cache_seconds";
}
...
server {
    ...
    add_header Cache-Control $cache_control;
    ...
}

If the result of $upstream_http_adr_private_cache_seconds to $cache_control translation will be an empty string, nginx would not set Cache-Control (or any other HTTP header when an empty value passed as parameter value of add_header directive) at all.

Update

OP asked additional question:

if a Cache-Control: public header already exists, then both headers remain (one public, one private), is there a way to remove the public one only if the custom header is found?

Here is the solution:

map $upstream_http_adr_private_cache_seconds $cache_control {
    ""       $upstream_http_cache_control; # use original upstream Cache-Control header if no custom header is given
    default  "private, max-age=$upstream_http_adr_private_cache_seconds";
}
...
server {
    ...
    proxy_hide_header Cache-Control; # clear Cache-Control header that came from upstream
    add_header Cache-Control $cache_control; # set Cache-Control header again
    ...
}