Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Varnish for PHP developers (LaraconEU 2016)

Varnish for PHP developers (LaraconEU 2016)

Varnish has the ability to reduce server load, improve page speeds and help scale an application from a couple of hundred users to tens of thousands. But Varnish isn't easy.

This talk guides you through what Varnish is, how it works and leaves you with practical pointers to implement it on your own infrastructure.

Mattias Geniar

August 23, 2016
Tweet

More Decks by Mattias Geniar

Other Decks in Technology

Transcript

  1. GET / HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Host: ma.ttias.be

    User-Agent: Chrome/52 Cookie: laravel_session=eyJpdiI6...
  2. RESPONSE HEADERS HTTP/1.1 200 OK Cache-Control: private, max-age=0 Content-Encoding: gzip

    Content-Length: 9944 Content-Type: text/html; charset=UTF-8 Server: Apache Date: Tue, 23 Aug 2016 14:15:50 GMT
  3. VARNISH CONFIGURATION LANGUAGE $ cat /etc/varnish/default.vcl vcl 4.0; sub vcl_recv

    { } sub vcl_backend_response { } sub vcl_backend_fetch { } sub vcl_deliver { }
  4. MANIPULATING THE CLIENT REQUEST sub vcl_recv { # Only cache

    GET or HEAD requests. if (req.method != "GET" && req.method != "HEAD") { return (pass); } # Remove any Google Analytics based cookies set req.http.Cookie = regsuball(req.http.Cookie, "__utm.=[^;]+(; )?", ""); set req.http.Cookie = regsuball(req.http.Cookie, "_ga=[^;]+(; )?", ""); ... }
  5. MANIPULATING THE CLIENT REQUEST sub vcl_recv { if (req.http.host ~

    "^(www\.)?domain\.tld$" || req.http.host ~ "test"){ set req.backend = specific_backend; return (pass); } }
  6. MANIPULATING THE CLIENT REQUEST sub vcl_recv { if (req.url ~

    "^[^?]*\.(css|gif|gz|ico|jpeg|jpg|js|png|xml)(\?.*)?$") { unset req.http.Cookie; return (lookup); } }
  7. MANIPULATING THE RESPONSE sub vcl_backend_response { # Don't cache 50x

    responses if (beresp.status == 500 || beresp.status == 502 || beresp.status == 503) { return (abandon); } # Allow stale content, in case the backend goes down. # make Varnish keep all objects for 2 hours beyond their TTL set beresp.grace = 2h; }
  8. MANIPULATING THE DELIVERY sub vcl_deliver { if (obj.hits > 0)

    { set resp.http.X-Cache = "HIT"; } else { set resp.http.X-Cache = "MISS"; } set resp.http.X-Cache-Hits = obj.hits; unset resp.http.X-Powered-By; unset resp.http.Server; }
  9. DETERMINING HASH KEYS sub vcl_hash { hash_data(req.url); // ie: /blog/your-clickbait-title

    hash_data(req.http.host); // ie: ma.ttias.be hash_data(req.http.Cookie); // ie: language=nl }
  10. CHALLENGE: URLS sub vcl_recv { # Sort all query arguments

    set req.url = std.querysort(req.url); // ie: "?lang=nl&p=5" == "?p=5&lang=nl" # Remove GA tags: domain.tld?utm_source=email&utm_medium=buffer&... set req.url = regsuball(req.url, "(utm_source|utm_medium|...)=(...)", ""); }
  11. CHALLENGE: COOKIES import cookie; sub vcl_recv { # Use libvmod-cookie,

    parse the "Cookie:" header from the client cookie.parse(req.http.cookie); # Filter all except these cookies from it cookie.filter_except("laravel_session,custom_cookie"); # Set the "Cookie:" header back to the parsed/filtered value set req.http.cookie = cookie.get_string(); }
  12. CHALLENGE: HOST sub vcl_recv { # domain.tld vs www.domain.tld, force

    www subdomain if (req.http.host == "domain.tld") { return (synth(720, "http://www.domain.tld")); } } sub vcl_synth { if (resp.status == 720) { set resp.http.Location = resp.reason; set resp.status = 301; return (deliver); } }
  13. CACHING API REQUESTS sub vcl_hash { hash_data(req.url); // ie: /api/companies/5/products

    hash_data(req.http.host); // ie: ma.ttias.be if (req.http.Authorization) { hash_data(req.http.Authorization); // ie: Bearer Access-Token } if (req.http.X-Custom-Header) { hash_data(req.http.X-Custom-Header); // ie: app specific } }
  14. PURGE REQUEST acl purge { "localhost"; "192.168.55.0"/24; } sub vcl_recv

    { # allow PURGE from localhost and 192.168.55... if (req.method == "PURGE") { if (!client.ip ~ purge) { return(synth(405,"Not allowed.")); } return (purge); } } $ curl -X PURGE -H "Host: domain.tld" http://31.193.180.217/blog/page1
  15. BAN REQUEST $ varnishadm \ -S /etc/varnish/secret \ "ban req.http.host

    ~ (www.)?domain.tld" $ varnishadm \ -S /etc/varnish/secret \ -T 31.193.180.217:6082 \ "ban req.http.host ~ www.domain.tld && req.url ~ .css" $ cat /etc/varnish/secret yb9exW6O57c77b9T9lHxkLAShsJ-QbXAdB7CMlGegGK1Q5ViJ5NTwRW
  16. <html> <body> <div class="navi"> <esi:include src="/partial/navi" /> <?php /* include

    'footer.php'; */ ?> </div> <div class="content"> <esi:include src="/partial/content" /> </div> <div class="content"> <!-- load content block twice --> <esi:include src="/partial/content" /> </div> </body> </html>
  17. TTL: HOW LONG TO CACHE? header("Cache-Control: public, max-age=900"); sub vcl_backend_response

    { if (bereq.url ~ "/blog/post-on-hackernews") { set beresp.ttl = 600s; return (deliver); } }
  18. GRACE MODE sub vcl_backend_response { # All objects get a

    10 minute cache lifetime set beresp.ttl = 10m; # But just in case, keep objects in cache for an extra 20 min # Can be served to clients while fetching new content # Total time object can be in the cache: 10min + 20min = 30min set beresp.grace = 20m; }