caddyserver/cache-handler

How does stale work?

Suven opened this issue · 7 comments

Suven commented

Hey!

I suspected that stale would ship cached content pass its TTL:

  • User requests /foo
  • /foo is cached with TTL 60 and the key has an age of 65
  • Caddy responds with /foo from cache
  • Caddy gets a fresh /foo and stores it in cache instead

But that does not seem to be the case. (btw, this is how varnish behaves when setting a grace). So what does stale do?

Hey @Suven actually, the cache-handler uses a stale response if the context allows it.
You must have to set the stale directive (by default the stale is equal to 0, and the ttl directive is equal to 2 minutes). If you don't set any of ttl and stale, the stale value would be 2 minutes (the default ttl value) + 0 second and it will expire at the same time as the ttl expires.
You have to set the max-stale directive in your request Cache-Control HTTP header to tell to the server that you (as a client) you allow the stale response.

If your stale response contains a stale-while-revalidate Cache-Control directive it will serve the stale response to the client and revalidate it with the upstream server in background.
Or if your stale response contains at least one of the must-revalidate, no-cache or need-revalidation Cache-Control directive it will revalidate with the server before sending the response to the client.
It will check that the stale response can be served as stale otherwise.

That's how it works for now, but using the bypass directive we could hack that behaviour and bypass the checks.
This test check that it should work following the RFC https://github.com/caddyserver/cache-handler/blob/master/httpcache_test.go#L349-L432

You have to set the max-stale directive in your request Cache-Control HTTP header to tell to the server that you (as a client) you allow the stale response.

Could this be the reason why I'm getting Cache-Status: Souin; fwd=uri-miss; key=GET-https-example.com-/; detail=INVALID-RESPONSE-CACHE-CONTROL?

My frontend is in swr and responds with Cache-Control: s-maxage=120, stale-while-revalidate

My caddyfile looks like:

{
  cache
  order cache before rewrite
}

example.com {
  cache
  encode gzip {
    match {
      header !Content-Encoding
    }
  }
  reverse_proxy {upstream 3000}
}

Or is it something else? I'm ferly new to caddy and caching in the reverse proxies

So, changing the frontend behavior to specify the stale value as Cache-Control: s-maxage=120, stale-while-revalidate=120 fixed the issue, but now I'm curious on why this happens.
I mean, since Souin already defaults the stale timer to ttl value, if not defined, should it do the same while reading stale-while-revalidate when there is a correct value for s-maxage, instead of triggering the invalid cache control?

Hello @Sandros94, s-maxage=100 tells the response should be stored for 100 seconds on shared caches (public/shared caches should not store authenticated requests/responses).The directive stale-while-revalidate should contains must contains a value stale-while-revalidate=999 to allow responses to be served only if they're stale for less than 999 seconds.

The directive stale-while-revalidate should contains must contains a value stale-while-revalidate=999 to allow responses to be served only if they're stale for less than 999 seconds.

But reading the related RFC example the behavior that I understand is:

  • If stale-while-revalidate has a value, then the total TTL of the cache is swr-value + max-age (s-maxage overrides the max-age iirc).
  • But if stale-while-revalidate has no value, then the TTL of the cache should be considered on the s-maxage value alone.

Or am I reading the documentation wrongly?

public/shared caches should not store authenticated requests/responses

I'm new to caching in reverse proxies and thankfully this is something I read everywhere. But do you have, by any change, something to read on the topic of authenticated requests/responses? I do plan to experiment with this and I wanted to boost some backends that are generally slow. Or should I completely ditch this approach?

P.S.: to disable caching for a particular route is it enough to add no-store? What if I wanted to store it privately on the browser but not the reverse proxy/cdn?