0

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 ?

adrianTNT
  • 1,007
  • 5
  • 21
  • 41

1 Answers1

1

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
    ...
}
adrianTNT
  • 1,007
  • 5
  • 21
  • 41
Ivan Shatsky
  • 2,360
  • 2
  • 6
  • 17
  • It works (thank you), but ... if a `Cache-Control: public` 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 ? I am lost at even trying to explain it. I did realize it doesn't work like programming but I am new to Nginx. – adrianTNT Feb 21 '20 at 00:21
  • 1
    @adrianTNT Can you test an updated answer? – Ivan Shatsky Feb 21 '20 at 00:47
  • It seems to work perfectly now, I will do more tests. `proxy_clear_header` was unknown to my server, you meant `proxy_hide_header` right ? (I edited your answer) :) – adrianTNT Feb 21 '20 at 00:56
  • It feels like we reinvented the wheel, should be common to want your own cache servers to cache your content but not the other external parties, isn't there some other way to allow the reverse proxy cache the content while maybe directly sending the final `private` directive from the origin server ? – adrianTNT Feb 21 '20 at 01:01
  • [Here is](https://www.nginx.com/blog/nginx-caching-guide/) detailed article about nginx caching, maybe `proxy_ignore_headers Cache-Control;` will do the trick. Can you test it? – Ivan Shatsky Feb 21 '20 at 01:12
  • I tested a bit, `proxy_ignore_headers Cache-Control;` made the cache server pass exact same origin headers to the final client, but it ignored the `Cache-Control` like `max-age=X` , so it didn't cache the page (ignored the header) Maybe I will experiment more, but for now I will use your solution. – adrianTNT Feb 21 '20 at 03:11
  • From the article given above: The [`proxy_cache_valid`](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache_valid) directive enforces an expiration for the cached data and is required if ignoring `Cache-Control` headers. NGINX does not cache files that have no expiration. – Ivan Shatsky Feb 21 '20 at 03:20
  • yes, but `proxy_cache_valid` is a kind of fixed cache time; works for smaller, more static sites. Mine has many listings with different cache time. – adrianTNT Feb 21 '20 at 06:00
  • Ivan, could you please have a look at a second question, I seen you are familiar with these settings, I am debugging for days: https://serverfault.com/questions/1003427/nginx-cache-same-url-first-returns-miss-to-all-chrome-curl-and-wget – adrianTNT Mar 03 '20 at 20:31