Slide 1

Slide 1 text

Varnish In Action By Thijs Feryn

Slide 2

Slide 2 text

Slow websites suck

Slide 3

Slide 3 text

Web performance is an essential part of the user experience

Slide 4

Slide 4 text

Down Slowdown ~ downtime

Slide 5

Slide 5 text

Hi, I’m Thijs

Slide 6

Slide 6 text

I’m @ThijsFeryn on Twitter

Slide 7

Slide 7 text

I’m an Evangelist At

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

https://www.facebook.com/fcbruges.support/

Slide 11

Slide 11 text

Mo money Mo servers

Slide 12

Slide 12 text

Cache

Slide 13

Slide 13 text

Don’t recompute if the data hasn’t changed

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

Normally User Server

Slide 16

Slide 16 text

With Varnish User Varnish Server

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

Why is it so good?

Slide 19

Slide 19 text

Smart kernel tricks

Slide 20

Slide 20 text

Stores HTTP output in memory*

Slide 21

Slide 21 text

Respects HTTP best practices

Slide 22

Slide 22 text

Varnish Configuration Language

Slide 23

Slide 23 text

What about TLS/SSL?

Slide 24

Slide 24 text

I'll talk about Varnish 4

Slide 25

Slide 25 text

Quick install & configure

Slide 26

Slide 26 text

apt-get install apt-transport-https curl https://repo.varnish-cache.org/GPG-key.txt | apt-key add - echo "deb https://repo.varnish-cache.org/debian/ jessie varnish-4.1"\ >> /etc/apt/sources.list.d/varnish-cache.list apt-get update apt-get install varnish Install on Linux (Debian) There's stuff for Ubuntu, RHEL & CentOS too

Slide 27

Slide 27 text

-a :80 \ -a :81,PROXY \ -T localhost:6082 \ -f /etc/varnish/default.vcl \ -S /etc/varnish/secret \ -s malloc,3g Simple config

Slide 28

Slide 28 text

vcl 4.0; backend default { .host = "localhost"; .port = "8080"; } Minimal VCL

Slide 29

Slide 29 text

Varnish speaks HTTP

Slide 30

Slide 30 text

✓Idempotence ✓State ✓Expiration ✓Conditional requests ✓Cache variations Varnish speaks HTTP

Slide 31

Slide 31 text

✓GET ✓HEAD -POST -PUT -PATCH -DELETE -TRACE -OPTIONS Idempotence Only cache GET & HEAD

Slide 32

Slide 32 text

✓Doesn't cache when cookies are present ✓Doesn't cache when cookies are set ✓Doesn't cache when Authorization headers are present State Avoids caching user- specific content Affects hit rate

Slide 33

Slide 33 text

Expiration Expires: Sat, 09 Sep 2017 14:30:00 GMT Cache-control: public, max-age=3600, s-maxage=86400 Cache-control: private, no-cache, no-store

Slide 34

Slide 34 text

Expiration precedence 1. beresp.ttl (VCL) 2. s-maxage 3. max-age 4. expires 5. 120 seconds by default

Slide 35

Slide 35 text

Conditional requests HTTP/1.1 200 OK Host: localhost Etag: 7c9d70604c6061da9bb9377d3f00eb27 Content-type: text/html; charset=UTF-8 Hello world output GET /if_none_match.php HTTP/1.1 Host: localhost User-Agent: curl/7.48.0

Slide 36

Slide 36 text

Conditional requests HTTP/1.0 304 Not Modified Host: localhost Etag: 7c9d70604c6061da9bb9377d3f00eb27 GET /if_none_match.php HTTP/1.1 Host: localhost User-Agent: curl/7.48.0 If-None-Match: 7c9d70604c6061da9bb9377d3f00eb27

Slide 37

Slide 37 text

Conditional requests HTTP/1.1 200 OK Host: localhost Last-Modified: Fri, 22 Jul 2016 10:11:16 GMT Content-type: text/html; charset=UTF-8 Hello world output GET /if_none_match.php HTTP/1.1 Host: localhost User-Agent: curl/7.48.0

Slide 38

Slide 38 text

Conditional requests HTTP/1.0 304 Not Modified Host: localhost Last-Modified: Fri, 22 Jul 2016 10:11:16 GMT GET /if_none_match.php HTTP/1.1 Host: localhost User-Agent: curl/7.48.0 If-Modified-Since: Fri, 22 Jul 2016 10:11:16 GMT

Slide 39

Slide 39 text

✓Hostname (or IP) ✓URL Cache variations Identify object in cache

Slide 40

Slide 40 text

Cache variations GET / HTTP/1.1 Host: localhost Accept-Language: nl HTTP/1.1 200 OK Host: localhost Vary: Accept-Language Hallo, deze pagina is in het Nederlands geschreven.

Slide 41

Slide 41 text

Built-in VCL cheat sheet

Slide 42

Slide 42 text

✓Only GET & HEAD ✓No Cookie ✓No Authorization Built-in VCL (frontend) Lookup in cache Otherwise pass and don't cache

Slide 43

Slide 43 text

✓No Set-Cookie ✓TTL > 0 ✓No "no-cache", "private", "no-store" Built-in VCL (backend) Store in cache Otherwise blacklist for 120s

Slide 44

Slide 44 text

The actual VCL code

Slide 45

Slide 45 text

sub vcl_recv { if (req.method == "PRI") { /* We do not support SPDY or HTTP/2.0 */ return (synth(405)); } if (req.method != "GET" && req.method != "HEAD" && req.method != "PUT" && req.method != "POST" && req.method != "TRACE" && req.method != "OPTIONS" && req.method != "DELETE") { /* Non-RFC2616 or CONNECT which is weird. */ return (pipe); } if (req.method != "GET" && req.method != "HEAD") { /* We only deal with GET and HEAD by default */ return (pass); } if (req.http.Authorization || req.http.Cookie) { /* Not cacheable by default */ return (pass); } return (hash); }

Slide 46

Slide 46 text

sub vcl_pipe { # By default Connection: close is set on all piped requests, to stop # connection reuse from sending future requests directly to the # (potentially) wrong backend. If you do want this to happen, you can undo # it here. # unset bereq.http.connection; return (pipe); } sub vcl_pass { return (fetch); } sub vcl_hash { hash_data(req.url); if (req.http.host) { hash_data(req.http.host); } else { hash_data(server.ip); } return (lookup); }

Slide 47

Slide 47 text

sub vcl_purge { return (synth(200, "Purged")); } sub vcl_hit { if (obj.ttl >= 0s) { // A pure unadultered hit, deliver it return (deliver); } if (obj.ttl + obj.grace > 0s) { // Object is in grace, deliver it // Automatically triggers a background fetch return (deliver); } // fetch & deliver once we get the result return (miss); } sub vcl_miss { return (fetch); } sub vcl_deliver { return (deliver); }

Slide 48

Slide 48 text

sub vcl_backend_fetch { return (fetch); } sub vcl_backend_response { if (beresp.ttl <= 0s || beresp.http.Set-Cookie || beresp.http.Surrogate-control ~ "no-store" || (!beresp.http.Surrogate-Control && beresp.http.Cache-Control ~ "no-cache|no- store|private") || beresp.http.Vary == "*") { /* * Mark as "Hit-For-Pass" for the next 2 minutes */ set beresp.ttl = 120s; set beresp.uncacheable = true; } return (deliver); }

Slide 49

Slide 49 text

Reality sucks

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

Cache-control ?

Slide 53

Slide 53 text

Legacy

Slide 54

Slide 54 text

Write VCL

Slide 55

Slide 55 text

Normalize

Slide 56

Slide 56 text

vcl 4.0; import std; sub vcl_recv { set req.http.Host = regsub(req.http.Host, ":[0-9]+", ""); set req.url = std.querysort(req.url); if (req.url ~ "\#") { set req.url = regsub(req.url, "\#.*$", ""); } if (req.url ~ "\?$") { set req.url = regsub(req.url, "\?$", ""); } if (req.restarts == 0) { if (req.http.Accept-Encoding) { if (req.http.User-Agent ~ "MSIE 6") { unset req.http.Accept-Encoding; } elsif (req.http.Accept-Encoding ~ "gzip") { set req.http.Accept-Encoding = "gzip"; } elsif (req.http.Accept-Encoding ~ "deflate") { set req.http.Accept-Encoding = "deflate"; } else { unset req.http.Accept-Encoding; } } } }

Slide 57

Slide 57 text

sub vcl_recv { if (req.url ~ "^/status\.php$" || req.url ~ "^/update\.php$" || req.url ~ "^/admin$" || req.url ~ "^/admin/.*$" || req.url ~ "^/user$" || req.url ~ "^/user/.*$" || req.url ~ "^/flag/.*$" || req.url ~ "^.*/ajax/.*$" || req.url ~ "^.*/ahah/.*$") { return (pass); } } URL blacklist

Slide 58

Slide 58 text

sub vcl_recv { if (req.url ~ "^/products/?" return (hash); } } URL whitelist

Slide 59

Slide 59 text

Cookies

Slide 60

Slide 60 text

vcl 4.0; sub vcl_recv { set req.http.Cookie = regsuball(req.http.Cookie, "has_js=[^;]+(; )?", ""); set req.http.Cookie = regsuball(req.http.Cookie, "__utm.=[^;]+(; )?", ""); set req.http.Cookie = regsuball(req.http.Cookie, "_ga=[^;]+(; )?", ""); set req.http.Cookie = regsuball(req.http.Cookie, "_gat=[^;]+(; )?", ""); set req.http.Cookie = regsuball(req.http.Cookie, "utmctr=[^;]+(; )?", ""); set req.http.Cookie = regsuball(req.http.Cookie, "utmcmd.=[^;]+(; )?", ""); set req.http.Cookie = regsuball(req.http.Cookie, "utmccn.=[^;]+(; )?", ""); set req.http.Cookie = regsuball(req.http.Cookie, "__gads=[^;]+(; )?", ""); set req.http.Cookie = regsuball(req.http.Cookie, "__qc.=[^;]+(; )?", ""); set req.http.Cookie = regsuball(req.http.Cookie, "__atuv.=[^;]+(; )?", ""); set req.http.Cookie = regsuball(req.http.Cookie, "^;\s*", ""); if (req.http.cookie ~ "^\s*$") { unset req.http.cookie; } } Remove tracking cookies

Slide 61

Slide 61 text

vcl 4.0; sub vcl_recv { if (req.http.Cookie) { set req.http.Cookie = ";" + req.http.Cookie; set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";"); set req.http.Cookie = regsuball(req.http.Cookie, ";(PHPSESSID)=", "; \1="); set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", ""); set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", ""); if (req.http.cookie ~ "^\s*$") { unset req.http.cookie; } } } Only keep session cookie

Slide 62

Slide 62 text

sub vcl_recv { if (req.http.Cookie) { set req.http.Cookie = ";" + req.http.Cookie; set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";"); set req.http.Cookie = regsuball(req.http.Cookie, ";(language)=", "; \1="); set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", ""); set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", ""); if (req.http.cookie ~ "^\s*$") { unset req.http.cookie; return(pass); } return(hash); } } sub vcl_hash { hash_data(regsub( req.http.Cookie, "^.*language=([^;]*);*.*$", "\1" )); } Language cookie cache variation

Slide 63

Slide 63 text

Or just use vmod_cookie

Slide 64

Slide 64 text

Alternative language cache variation

Slide 65

Slide 65 text

sub vcl_hash { hash_data(req.http.Accept-Language); } HTTP/1.1 200 OK Host: localhost Vary: Accept-Language Hash language Or just use

Slide 66

Slide 66 text

Cookie guidelines

Slide 67

Slide 67 text

✓Don't throw everything in a session ✓Use dedicated cookies ✓Strip off tracking cookies in VCL ✓Match cookies in VCL & make decisions ✓Perform cache variations on cookie values ✓Only use session cookies when you really have to ✓Use JWT to store session state client-side Cookie guidelines

Slide 68

Slide 68 text

Block caching

Slide 69

Slide 69 text

No content

Slide 70

Slide 70 text

Code renders single HTTP response

Slide 71

Slide 71 text

Lowest common denominator: no cache

Slide 72

Slide 72 text

Edge Side Includes ✓Placeholder ✓Parsed by Varnish ✓Output is a composition of blocks ✓State per block ✓TTL per block

Slide 73

Slide 73 text

sub vcl_recv { set req.http.Surrogate-Capability = "key=ESI/1.0"; } sub vcl_backend_response { if (beresp.http.Surrogate-Control ~ "ESI/1.0") { unset beresp.http.Surrogate-Control; set beresp.do_esi = true; } } Edge Side Includes

Slide 74

Slide 74 text

ESI vs AJAX

Slide 75

Slide 75 text

Choose wisely ESI, parsed by Varnish HInclude, parsed by Javascript

Slide 76

Slide 76 text

Measure the pressure

Slide 77

Slide 77 text

✓Varnishlog ✓Varnishtop ✓Varnishstat Measure the pressure

Slide 78

Slide 78 text

Breaking news isn't breaking

Slide 79

Slide 79 text

Purging

Slide 80

Slide 80 text

acl purge { "localhost"; "127.0.0.1"; "::1"; } sub vcl_recv { if (req.method == "PURGE") { if (!client.ip ~ purge) { return (synth(405, “Not allowed.”)); } return (purge); } } Purging

Slide 81

Slide 81 text

acl purge { "localhost"; "127.0.0.1"; "::1"; } sub vcl_backend_response { set beresp.http.x-url = bereq.url; set beresp.http.x-host = bereq.http.host; } sub vcl_deliver { unset resp.http.x-url; unset resp.http.x-host; } sub vcl_recv { if (req.method == "PURGE") { if (!client.ip ~ purge) { return (synth(405, "Not allowed")); } if(req.http.x-purge-regex) { ban("obj.http.x-host == " + req.http.host + " && obj.http.x-url ~ " + req.http.x-purge-regex); } else { ban("obj.http.x-host == " + req.http.host + " && obj.http.x-url == " + req.url); } return (synth(200, "Purged")); } } Banning

Slide 82

Slide 82 text

Banning curl -XPURGE "http://example.com/products" curl -XPURGE -H "x-purge-regex:/products" \ "http://example.com" varnishadm> ban obj.http.x-host == example.com && obj.http.x-url ~ ^/product/[0-9]+/details

Slide 83

Slide 83 text

Write less VCL Write more code

Slide 84

Slide 84 text

https://github.com/ThijsFeryn/ cacheable-site-silex/tree/v2

Slide 85

Slide 85 text

No content

Slide 86

Slide 86 text

No content

Slide 87

Slide 87 text

https://twitter.com/thijsferyn https://instagram.com/thijsferyn https://blog.feryn.eu https://talks.feryn.eu https://book.feryn.eu https://youtube.com/thijsferyn https://soundcloud.com/thijsferyn http://itunes.feryn.eu