Varnish Cache Configuration and VCL Guide - 夜莺博客

Varnish Cache Configuration and VCL Guide

Varnish sits in front of an application server and answers most requests without touching it — the classic outcome is a reduction from full application load to single-digit percentages for anonymously-cached content. The catch is that Varnish only caches what you tell it to: the VCL configuration language is where the entire caching policy lives, and a two-line mistake (usually "looks like a cookie, so it must be private") drops the hit rate to near zero. This guide builds a realistic VCL covering the decisions that actually move hit rate.

How a request flows through VCL

client -> vcl_recv -> [vcl_hash] -> cache lookup
                                      hit  -> vcl_hit  -> vcl_deliver -> client
                                      miss -> vcl_miss -> backend -> vcl_backend_response
                                                                   -> vcl_deliver -> client
built-in vcl_* routines run if you do not define your own; every routine
ends with a return(action) that decides what happens next.

The seven decisions that matter are return(pass), return(hash), return(pipe), return(deliver), return(fetch), return(restart) and return(synth).

Start with the defaults plus a backend

vcl 4.1;

import std;

backend default {
  .host = "127.0.0.1";
  .port = "8080";
  .connect_timeout = 1s;
  .first_byte_timeout = 15s;
  .between_bytes_timeout = 5s;
  .probe = {
    .url = "/healthz";
    .timeout = 1s;
    .interval = 5s;
    .window = 5;
    .threshold = 3;
  }
}

vcl 4.1 must be the first line of every file. Health probing here is what stops Varnish from sending traffic to a dead backend.

Deciding what to cache (vcl_recv)

sub vcl_recv {
  # never cache authenticated or management paths
  if (req.url ~ "^/(admin|login|api/private)") { return (pass); }

  # normalise the host header
  set req.http.host = regsub(req.http.host, "^www\.", "");

  # strip tracking params so they do not fragment the cache
  if (req.url ~ "([?&])(utm_[a-z]+|gclid|fbclid)=") {
    set req.url = regsuball(req.url, "([?&])(utm_[a-z]+|gclid|fbclid)=[^&]*", "\1");
    set req.url = regsub(req.url, "[?&]$", "");
  }

  # assets are safe to cache even for logged-in users
  if (req.url ~ "\.(css|js|png|jpg|jpeg|webp|svg|woff2?)$") {
    unset req.http.cookie;
  }

  if (req.method != "GET" && req.method != "HEAD") { return (pass); }
  if (req.http.Authorization) { return (pass); }
}

URL normalisation is the highest-leverage change for most sites: a single tracking parameter turns one cached object into hundreds, and stripping it typically recovers tens of percentage points of hit rate.

Deciding how long (vcl_backend_response)

sub vcl_backend_response {
  # do not cache error pages
  if (beresp.status >= 500 || beresp.status == 403) {
    set beresp.uncacheable = true;
    set beresp.ttl = 10s;
    return (deliver);
  }

  # believe the origin when it says private/no-store
  if (beresp.http.Cache-Control ~ "(private|no-store)") {
    set beresp.uncacheable = true;
    return (deliver);
  }

  # default TTLs by content type
  if (beresp.http.content-type ~ "text/html")        { set beresp.ttl = 2m; }
  else if (beresp.http.content-type ~ "image/")      { set beresp.ttl = 1d; }
  else if (beresp.http.content-type ~ "javascript")  { set beresp.ttl = 1h; }

  # grace: serve slightly stale content while refreshing in the background
  set beresp.grace = 30m;
}

beresp.grace is underused: with a grace period, an origin that is slow or briefly down still gets answered from stale-but-valid content instead of producing errors. Combined with request coalescing (beresp.do_stream and hit-for-pass decisions) it removes most thundering-herd behaviour.

Invalidation and purging

# allow PURGE only from localhost or a trusted admin network
sub vcl_recv {
  if (req.method == "PURGE") {
    if (!client.ip ~ localhost && client.ip !~ purge_acl) {
      return (synth(405, "Not allowed"));
    }
    return (purge);
  }
  if (req.method == "BAN") {
    ban("obj.http.x-host == " + req.http.host + " && obj.http.x-url ~ " + req.url);
    return (synth(200, "Banned"));
  }
}

sub vcl_backend_response {
  set beresp.http.x-url  = bereq.url;
  set beresp.http.x-host = bereq.http.host;
  # remove the markers before sending to the client
}
sub vcl_deliver {
  unset resp.http.x-url;
  unset resp.http.x-host;
  if (obj.hits > 0) { set resp.http.X-Cache = "HIT"; }
  else              { set resp.http.X-Cache = "MISS"; }
}

ban is the mechanism for invalidating whole families of objects (a host, a URL pattern) without touching the origin; purge removes one exact object. The X-Cache header is what makes hit-rate debugging trivial from the outside.

Verifying behaviour

varnishstat -1 | grep -E "cache_hit|cache_miss|n_object|backend_fail"
varnishlog -g request -q "ReqURL ~ '^/product/'" | head -50
varnishncsa -F "%h %m %U %s %{Varnish:hitmiss}x"
varnishadm ban.list
varnishadm param.show default_ttl
Pitfall                              Symptom
Cookie not stripped                  cache_hit near zero, huge n_object
No Vary handling                     wrong content served (mobile vs desktop)
Unnormalised query strings           near-zero hit rate on listing pages
TTL too long on personalised pages   stale prices, angry customers
PURGE open to the internet           anyone can hammer your origin

For the TLS-terminating layer in front, pair Varnish with nginx (nginx reverse proxy) or Caddy (Caddy configuration); Varnish itself speaks plain HTTP by default.

FAQ

Q: Varnish or a CDN? They solve different rules: a CDN handles geography and offload at the edge, Varnish handles your own origin-side caching policy. Many sites run both, with Varnish as the CDN's origin shield.
Q: Can Varnish cache HTTPS? Not natively; terminate TLS in front (hitch, nginx, Caddy) or use the commercial Varnish Enterprise build.
Q: How do I cache logged-in users? Careful VCL that hashes on a session cookie while keeping the object public — a common pattern for read-heavy catalogues, but it requires a purge strategy tied to the user's actions.

原文链接:https://varnish-cache.readthedocs.io/reference/vcl.html