How to cache things longer on Varnish than on the client

by , , revisited on

We have by far the largest RPM repository with NGINX module packages and VMODs for Varnish. If you want to install NGINX, Varnish, and lots of useful performance/security software with smooth yum upgrades for production use, this is the repository for you.
Active subscription is required.

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";

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: