Boost Symfony with Varnish

Varnish is an HTTP accelerator designed for content-heavy dynamic web sites as well as heavily consumed APIs.

TYPE OF CACHES

BROWSER CACHE

CLIENT PROXY CACHE

REVERSE PROXY CACHE

HOW IT WORKS?

HOW IT WORKS?

Expiration

 

Cache-Control/Expires

HOW IT WORKS?

Validation

 

Last-Modified / If-Modified-Since
ETag / If-None-Match

HTTP EXPIRATION

HTTP EXPIRATION

HTTP EXPIRATION

HTTP VALIDATION

HTTP VALIDATION

HTTP VALIDATION

VCL

VCL

VCL - Varnish configuration Language

The VCL language is a small domain-specific language designed to be used to define request handling and document caching policies for the Varnish HTTP accelerator. When a new configuration is loaded, the varnishd management process translates the VCL code to C and compiles it to a shared object which is then dynamically linked into the server process.

STATE MACHINE

STATE MACHINE

STATE MACHINE

VCL_RECV

    # All unknown methods should be piped to the backend
    if (req.method != "GET" &&
        req.method != "HEAD" &&
        req.method != "PUT" &&
        req.method != "POST" &&
        req.method != "TRACE" &&
        req.method != "OPTIONS" &&
        req.method != "DELETE") {
        std.log("Unknown method " + req.method + " is piped to the backend");
        return(pipe);
    }
 
    # We only deal with GET and HEAD by default
    if (req.method != "GET" && req.method != "HEAD") {
        std.log("Method is " + req.method + " and passed to the backend directly");
        return(pass);
    }

VCL_RECV

    # This block will remove every unknown cookie except the server known cookies.
    # Known server cookies are: sid (early PHPSESSID), session_token
    if (req.http.Cookie && !req.http.X-Cookie) {
        set req.http.Cookie = ";" + req.http.Cookie;
        set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
        set req.http.Cookie = regsuball(req.http.Cookie, ";(sid|session_token|ssid)=", "; \1=");
        set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");
 
        # Clean empty Cookie header
        if (req.http.Cookie == "") {
            unset req.http.Cookie;
        }
 
        # Save cookie to X-Cookie header
        set req.http.X-Cookie = req.http.Cookie;
    }
 
    # All internal subrequests that declared stateless will be made without any cookies
    if (req.url ~ "^/_internal/" && req.url ~ "stateless") {
        std.log("Request to the internal stateless block, will be made without any cookies");
        unset req.http.Cookie;
    }

VCL_RECV

    # Inform backend that we capable to process surrogates
    set req.http.Surrogate-Capability = "Varnish=ESI/1.0";
 
    # Always try to lookup, even for clients with cookies
    std.log("Trying to lookup in the cache");
    return(hash);

VCL_BACKEND_RESPONSE

    # Instruct Varnish to keep the objects in the cache beyond their TTL
    set beresp.grace = 24h;
 
    # Copy url to internal headers for lurker-friendly bans
    set beresp.http.X-Url  = bereq.url;
    set beresp.http.X-Host = bereq.http.host;
 
    # Enable processing of ESI if backend has reported that it want to be processed with ESI
    if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
        unset beresp.http.Surrogate-Control;
 
        // for Varnish >= 3.0
        set beresp.do_esi = true;
    }

VCL_BACKEND_RESPONSE

    # Cache 404s and 301s for 5 minutes to prevent a possible attack on backends
    if (beresp.status == 404 || beresp.status == 301) {
        std.log("Received " + beresp.status + " code from the backend. Cache it");
        set beresp.ttl = 5m;
        return(deliver);
    }
 
    # Do not cache anything with no-cache directive
    if (beresp.http.Cache-Control ~ "no-cache") {
        set beresp.uncacheable = true;
    }

VCL_HIT

sub vcl_hit {
 
    if (obj.ttl >= 0s) {
        # A pure unadultered hit, deliver it
        return (deliver);
    }
    # Graced delivery
    if (obj.ttl + obj.grace > 0s) {
        set req.http.X-Cache-Has-Stale = true;
        return (deliver);
    }
 
    # Fetch & deliver once we get the result
    return (fetch);
}

CACHE INVALIDATION

sub vcl_recv {
    # Restrict access to the PURGE method by "purge" ACL
    if (req.method == "PURGE") {
        if (!std.ip(regsub(req.http.X-Forwarded-For, "[, ].*$", ""), client.ip) ~ purge) {
            return (synth(405, "Not allowed."));
        }
        ban("obj.http.X-Host == " + req.http.host + " 
           && obj.http.X-Url ~ " + req.http.X-Purge-Regex);
        return (synth(200, "Purged. Host: " + req.http.host + 
           " Regex:" + req.http.X-Purge-Regex));
    }
}

LIVE REFRESH

sub vcl_recv {

    # Editors can reload cache by forcing page
    if (req.http.Cache-Control ~ "no-cache" &&
        std.ip(regsub(req.http.X-Forwarded-For, "[, ].*$", ""), client.ip) ~ editors) {
        std.log("Editor from IP: " + client.ip + " requests a cache refresh");
 
        std.log("Mark request as always miss from cache");
        set req.hash_always_miss = true;
        set req.http.X-Cache-Always-Miss = true;
    }
}

THANKS!

Boost Symfony with Varnish

By Alexander Lisachenko