Opened 4 years ago

Last modified 10 days ago

#1182 new enhancement

Responses with "no-cache" or "max-age=0" should be cached

Reported by:… Owned by:
Priority: minor Milestone:
Component: other Version: 1.9.x
Keywords: Cc:
uname -a: Linux geoff-XPS-8300 4.4.0-57-generic #78-Ubuntu SMP Fri Dec 9 23:50:32 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
nginx -V: nginx version: nginx/1.10.0 (Ubuntu)
built with OpenSSL 1.0.2g 1 Mar 2016
TLS SNI support enabled
configure arguments: --with-cc-opt='-g -O2 -fPIE -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -fPIE -pie -Wl,-z,relro -Wl,-z,now' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/ --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-ipv6 --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_addition_module --with-http_dav_module --with-http_geoip_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module --with-http_v2_module --with-http_sub_module --with-http_xslt_module --with-stream --with-stream_ssl_module --with-mail --with-mail_ssl_module --with-threads


I know the summary sounds contradictory, but I believe that caching these responses is allowed, when revalidation is enabled. These responses would always need to be revalidated.

Here's the relevant part of the HTTP spec:

       If the no-cache directive does not specify a field-name, then a
      cache MUST NOT use the response to satisfy a subsequent request
      without successful revalidation with the origin server. This
      allows an origin server to prevent caching even by caches that
      have been configured to return stale responses to client requests.

So, if "no-cache" is present, then nginx is allowed to cache the response, but must treat the cache entry as if it has already expired. The next lookup requires revalidation. I believe that "max-age=0" or "s-maxage=0" should be treated the same way, but I don't have a specific reference to the spec to justify my opinion.

We have an upstream server that is capable of returning 304 Not Modified much more quickly than it can generate the response body for a 200. We want to be able to use nginx's caching abilities to store the response body (especially to share that response body between users), but we also want to revalidate with the upstream server on every request. Right now we're working around this with "max-age=1" so that things expire quickly, but technically we want them to expire right away.

Also see "Pattern 2" on the first hit when searching "HTTP Cache Best Practices".

Change History (4)

comment:1 by micah.hainline@…, 11 days ago

Any update on this? This feels like a very legitimate problem. I have a server sending the following:

Cache-Control: max-age=0, public, must-revalidate;
ETag: W/"somestring"

And I expected it to cache that response when acting properly according to spec, and revalidate it with the server when it was called again. Any validation that of what's being said here, or a workaround if this problem still hasn't been fixed in the latest version?

comment:2 by Maxim Dounin, 11 days ago

Historically, nginx does not cache responses with "max-age=0", as well as responses with Expires in the past, as such responses are effectively stale and cannot be used to respond to future requests.

Since proxy_cache_revalidate support was added, there are might be some valid use cases when caching such responses might be beneficial with proxy_cache_revalidate enabled. This probably needs further investigation.

Just for the record, one of the first discussions can be seen in this thread (in Russian).

If you want to use proxy_cache_revalidate to revalidate responses immediately, a readily available workaround is to control caching with the X-Accel-Expires: @0 header instead.

comment:3 by micah.hainline@…, 10 days ago

Trying the following didn't work.

Cache-Control: max-age=0, public, must-revalidate;
ETag: W/"somestring"
X-Accel-Expires: @0

I'm assuming that max-age is still being used to determine that nginx will not cache in this case. We could change max-age=1 or some other value, but that would be counter to the spec and break other caches. Would that be the recommended workaround for this though?

comment:4 by Maxim Dounin, 10 days ago

X-Accel-Expires: @0

My bad, should be X-Accel-Expires: @1 (which is essentially 1 second after the epoch), as plain zero will be handled as "no expiration time set" elsewhere and no caching will happen.

Additionally, this should be either before the Cache-Control header to make sure nginx wont't disable caching due to max-age=0, or the Cache-Control header should be explicitly ignored with proxy_ignore_headers.

Note: See TracTickets for help on using tickets.