My CMS generates pretty complex pages and thus takes a little while to do so (about 2 seconds), which is well above my time budget to serve pages to the client.
However it's very cheap for me to know the current version of a given page and such it's very easy for me to say if a given version is still up-to-date. As such, I would like to be able to put a ETag-based strategy where every single request to a page needs to be revalidated but the server will reply in 10ms tops if the content didn't change.
For th is to be effective, I need to share this cache between all my clients. As long as the ETag gets revalidated, all my pages will stay identical for all users, so I can safely share their content.
In order to do so, my page emits a:
Cache-Control: public, no-cache, must-revalidate
ETag: W/"xxx"
When testing from a browser it works great: the page stays in cache and simply revalidates against the server every time I refresh the page, getting a 304 most of the time or a 200 when I change the content version.
All I need now is to share this cache between clients. Essentially:
- Phase A
- Client A sends a request to the proxy
- Proxy doesn't have in cache so asks the backend
- Backend replies 200 with an ETag
- Proxy replies 200 with an ETag
- Phase B
- Client B sends the same request to the proxy
- Proxy has in cache but must revalidate (because no-cache and must-revalidate and ETag)
- Backend replies with 304 (because the revalidation request includes the If-None-Match header with the cached ETag)
- Proxy replies 200 with an Etag
- Phase C
- Client A sends the same request again, this time with If-None-Match
- The proxy asks the backend with the provided If-None-Match header (not the cached one)
- The backend server replies 304
- The proxy replies 304
I've tried nginx but it requires lots of tweaking to even get it remotely working. Then I've tried Traefik before realizing that the caching middleware is part of the enterprise version. Then I've figured that Varnish seems to implement what I want.
So here I go with my Varnish configuration:
vcl 4.0;
backend default {
.host = "localhost";
.port = "3000";
}
backend api {
.host = "localhost";
.port = "8000";
}
sub vcl_recv {
if (req.url ~ "^/back/" || req.url ~ "^/_/") {
set req.backend_hint = api;
} else {
set req.backend_hint = default;
}
}
And of course... It didn't work.
When varying the Cache-Control
headers I either get the result from a shared cache but which isn't revalidated or just a pass-through to the client but never does it seem to keep the content in cache as I'd like it to.
What am I missing to get this shared cache/ETag re-validation logic in place? I suppose that I'm missing something obvious but can't figure out what.