1/55

HTTP Caching
with Varnish


Drupal Mountain Camp, Davos
March 8th, 2024


© David Buchmann







David Buchmann - david@liip.ch

PHP Engineer, Liip AG, Switzerland

But... why?

What is a reverse proxy again?

What could possibly go wrong?

httpstatusdogs.com

Overview




HTTP Refresher

HTTP: Browser chats with webserver

Request

1GET /path
2Accept-Encoding: text/html

Response

1HTTP/1.1 200 OK
2Content-Type: text/html
3 
4<html>...</html>

HTTP verbs

HTTP response codes

twitter.com/stevelosh/status/372740571749572610




HTTP Cache Control

Cache control headers

HTTP 1.1, RFC 2616, Sections 13.2 and 13.3

Cache Expiration

1Cache-Control: s-maxage=3600, max-age=900
2Expires: Sat, 10 Mar 2024 08:00:00 GMT
  1. s-maxage
  2. max-age
  3. Expires (HTTP 1.0 - avoid!)
  4. Default to default_ttl if nothing specified

max-age

  • max-age per block and other page elements
  • Application layer cache for individual elements
  • Drupal builds the HTTP header from lowest encountered max-age
  • http_response_header module for rule based HTTP headers (but mostly the Drupal caching framework provides enough tools)

Cache validation

ETag: 82901821233

If-None-Match: 82901821233

304 Not Modified

etag

  • Drupal core sets etag on cacheable responses (using the last modified timestamp)
  • Drupal handles the If-None-Match header
  • Drupal redundantly also does Last-Modified and If-Modified-Since

Do not cache

1Cache-Control: s-maxage=0, private, no-cache

Surrogate Control

Header specific for your reverse proxy, different from third party caches

1Cache-Control: no-store
2Surrogate-Control: max-age=3600

Resilience

Response

1Cache-Control: stale-while-revalidate=3600;
2Cache-Control: stale-if-error=3600;

Request

1Cache-Control: must-revalidate;

Default Varnish behaviour

Keep variants apart

Response content depends on request headers

Requests

1GET /resource
2Accept: application/json
1GET /resource
2Accept: text/xml

Response

1Vary: Accept



Varnish does what you tell it



Think carefully and test thoroughly

Varnish Configuration Language

VCL: Debug time to live

1sub vcl_backend_response {
2    set beresp.http.TTL = beresp.ttl;
3}

VCL: Two applications

01backend default {
02    .host = "127.0.0.1"; .port = "8080";}
03backend legacy {
04    .host = "127.0.0.1"; .port = "8000";}
05 
06sub vcl_recv {
07    if (req.url ~ "^/archive/") {
08        set req.backend_hint = legacy;
09    } else {
10        set req.backend_hint = default;
11    }
12}

VCL can do a lot of things


But first make your application behave correctly!


Advanced topics

Cache Invalidation

There are two hard things in computer science:

  1. Naming things
  2. Cache invalidation
  3. Off by one errors

Cache busting

1<link rel="stylesheet" href="/css/style.css?v1" type="text/css"/>
2...
3<script src="/js/scripts.js?v1"></script>

Explicit cache invalidation

Invalidation flavors

Communicating invalidation

purge module to invalidate external caches.
Plugin for specific reverse proxies and CDNs varnish_purge

Custom configuration for purge

01acl invalidators {
02    "localhost";
03}
04 
05if (req.method == "PURGE") {
06    if (!client.ip ~ invalidators) {
07        return (synth(405, "Not allowed"));
08    }
09    return (purge);
10}
11 
12...

Custom configuration for refresh

01acl invalidators {
02    "localhost";
03}
04 
05if (req.http.Cache-Control ~ "no-cache"
06    && client.ip ~ invalidators
07) {
08    set req.hash_always_miss = true;
09}
10 
11...

Banning

01vcl_backend_response {
02    set beresp.http.X-Url = bereq.url;
03    set beresp.http.X-Host = bereq.http.host;
04}
05 
06vcl_recv {
07  if (req.method == "BAN") {
08    if (!client.ip ~ invalidators) {
09      return (synth(405, "Not allowed"));
10    }
11    ban("obj.http.X-Host ~ " + req.http.X-Host
12      + " && obj.http.X-Url ~ " + req.http.X-Url
13    );
14  }
15}

Cache Tagging

1$response->withHeader('xkey', 'node:2 node:44');
1xkey.purge(req.http.xkey-purge);

You can also use BAN, but its much less efficient

Cache Tagging

  • The purge module can handle cache tags.
  • The varnish purge module documents tag invalidation with BAN - somebody should contribute xkey documentation ;-)



Edge Side Includes

Use Edge Side Includes

Like server side include, but on Varnish:

ESI error handling

Use Edge Side Includes

  • You could build something with the placeholder mechanism
  • The Advanced Varnish module provides ESI blocks (among other nice features)
  • There is an unmaintained ESI module.



Wrap-Up

Take-Aways

Thank you!


@dbu@phpc.social




Caching lists of content

Element  weight  <->  Element  weight
A9D22
B8A9
C7B8
D6C7
E5E5
F4F4
G3G3
H2H2
I1I1
Element  weight  <->  Element  weight
A9A9
B8B8
C7D6
D6E5
E5F4
F4G3
G3H2
H2I1
I1



Caching and Sessions

Strategies when Caching with Sessions