Say that you want Varnish to cache your resources forever but send headers to browsers so that they cache it at short time. It is maybe useful if you want to leverage client side (browser) cache for your dynamic pages.
Suppose that we have a blog post in WordPress. We want Varnish to cache it forever (until we PURGE it from cache by updating). And we want browser to cache it for one day only. How do we do that?
Solution #1. Adjust the backend
We have to write a plugin that will make use of Cache-Control header to cache things differently on Varnish and browsers.
The Cache-Control
header can contain a number of headers. Varnish evaluates it and looks for s-maxage
and max-age
. It will set the TTL to the value of s-maxage
if found. If s-maxage
isn’t found, it will use max-age
. If neither exist, it will use the Expires
header to set the ttl. If none of those headers exist, it will use the default TTL.
So in a nutshell, all we have to do is make WordPress send the following header:
Cache-Control: s-maxage=31536000, max-age=86400
The header above will instruct a browser to cache resource for 86400 seconds, while Varnish will cache for 31536000. This is because s-maxage
only applies to shared caches. Varnish evaluates it, while browsers don’t.
To implement this in WordPress, all we have to do, is add a few more lines of code to theme’s functions.php
:
add_action( 'send_headers', 'add_header_maxage' );
function add_header_maxage() {
header( 'Cache-Control: s-maxage=86400, max-age=31536000' );
}
Solution #2. Adjust Varnish VCL
You can also use Varnish VCL magic for the task.
Following along this post and given that you’re using Varnish 4:.
sub vcl_backend_response {
if (beresp.ttl > 0s) {
/* Remove Expires from backend, it's not long enough */
unset beresp.http.expires;
/* Set the clients TTL on this object */
set beresp.http.cache-control = "max-age=86400";
/* Set how long Varnish will keep it */
set beresp.ttl = 1y;
/* marker for vcl_deliver to reset Age: */
set beresp.http.magicmarker = "1";
}
}
sub vcl_deliver {
if (resp.http.magicmarker) {
/* Remove the magic marker */
unset resp.http.magicmarker;
/* By definition we have a fresh object */
set resp.http.age = "0";
}
}