Possible error on revalidate in ngx_http_upstream

Maxim Dounin mdounin at mdounin.ru
Thu Oct 14 17:33:28 UTC 2021


Hello!

On Thu, Oct 07, 2021 at 06:37:11PM +0200, Jiří Setnička wrote:

> I use nginx as a proxy with enabled cache. I encountered strange 
> behavior on revalidate.
> 
> When upstream does not return any caching headers it is ok - file is 
> cached with default cachetime and on revalidate the r->cache->valid_sec 
> is updated to now + default cachetime.
> 
> Also when upstream consistently returns caching headers it is still ok - 
> file is cached according to caching headers and on revalidate the 
> r->cache->valid_sec is updated by value from 304 response caching headers.
> 
> Problem is when upstream previously returned absolute caching headers on 
> 200 response (so the file is cached according to these headers and these 
> headers are saved into cache file on disk) but later it changed its 
> behavior and on 304 response it does not return any caching headers.
> In such case, I would expect that now + default cachetime would be used 
> as the new r->cache->valid_sec, but old absolute time is used instead 
> and this yields in revalidate on each request.

Per RFC 2616
(https://datatracker.ietf.org/doc/html/rfc2616#section-10.3.5):

:    The response MUST include the following header fields:
: 
:       ...
: 
:       - Expires, Cache-Control, and/or Vary, if the field-value might
:         differ from that sent in any previous response for the same
:         variant

That is, as long as Expires is not present in the 304 response, 
per RFC 2616 it is correct to assume that the Expires in the 
original response still applies.  This is what nginx currently 
does: if there are no Expires/Cache-Control in the 304 response, 
it uses Expires/Cache-Control from the original response, and 
there are none either, tries to use proxy_cache_valid.  With this 
behaviour it is basically not possible to "remove" cache control 
headers, it is only possible to redefine them to something new 
explicitly returned.

In RFC 7232 this was changed to
(https://datatracker.ietf.org/doc/html/rfc7232#section-4.1):

:    The server generating a 304 response MUST generate any of the
:    following header fields that would have been sent in a 200 (OK)
:    response to the same request: Cache-Control, Content-Location, Date,
:    ETag, Expires, and Vary.

This implies that the original response Expires and Cache-Control 
headers should not be used at all.  With this approach, Expires 
and Cache-Control headers can be safely removed (in theory).

Switching to RFC 7232 logic might be considered, though this 
should be done with care, as it can affect various upstream 
servers which follow RFC 2616 and does not generate 
Expire/Cache-Control if these are expected to match headers 
returned in the original response.

On the other hand, I'm not sure this will help in your case, since 
Expires headers in the past are already automatically ignored, see 
below.

> In ngx_http_upstream_test_next(...) in revalidate part there is firstly 
> cache time from upstream 304 response saved to temporal variable (valid 
> = r->cache->valid_sec) and then request is reinited and 
> r->cache->valid_sec is set according to headers in the cached file.
> Problem is when value == 0 (no caching info from upstream) and there is 
> an absolute time in the cached file headers.
> 
> This patch should fix this behavior - time computed from cached file is 
> used only when it is in the future otherwise, time calculated by 
> ngx_http_file_cache_valid(...) is used.

As long as Expires is in the past, r->cache->valid_sec is not set 
and remains 0, see ngx_http_upstream_process_expires().  As such, 
suggested patch is a nop as long as standard Expires and 
Cache-Control headers are used: nginx will ignore Expires from the 
original response automatically, and will use proxy_cache_valid 
instead.

Are you trying to address X-Accel-Expires with an absolute time in 
the past?  Note that it is known to be specifically used to 
achieve the "revalidate on each request" behaviour, and the 
suggested change will break this.  (Also, changing the 
X-Accel-Expires behaviour is better to be done in 
ngx_http_upstream_process_accel_expires(), rather than indirectly, 
in 304 response handling code.)

-- 
Maxim Dounin
http://mdounin.ru/


More information about the nginx-devel mailing list