Who is this guy? 1. Helped build the original HTML5 web app for the FT 2. Created our Origami component system 3. Ran FT Labs for 3 years 4. Now working with Nikkei to rebuild nikkei.com 5. Also W3C Technical Architecture Group 6. Live in Tokyo, Japan 2 Pic of me.
Edge side includes 6 alt="http://bak.example.com/2.html" onerror="continue"/> index.html my-news.html Cache-control: max-age=86400 Cache-control: private Server
The VCL way 1. Request and response bodies are opaque 2. Everything happens in metadata 3. Very restricted: No loops or variables 4. Extensible: some useful Fastly extensions include geo-ip and crypto 5. Incredibly powerful when used creatively 7
SOA Routing Send requests to multiple microservice backends This is great if... ● You have a microservice architecture ● Many backends, one domain ● You add/remove services regularly 1
SOA Routing in VCL 10 [ { name, paths, host, useSsl, }, … ] {{#each backends}} backend {{name}} { .port = "{{p}}"; .host = "{{h}}"; } {{/each}} let vclContent = vclTemplate(data); fs.writeFileSync( vclFilePath, vclContent, 'UTF-8' ); services.json Defines all the backends and paths that they control. routing.vcl.handlebars VCL template with Handlebars placeholders for backends & routing build.js Task script to merge service data into VCL template
SOA Routing: key tools and techniques ● Choose a backend: set req.backend = {{backendName}}; ● Match a route pattern: if (req.url ~ "{{pattern}}") ● Remember to set a Host header: set req.http.Host = "{{backendhost}}"; ● Upload to Fastly using FT Fastly tools ○ https://github.com/Financial-Times/fastly-tools 11
UA Targeting Return user-agent specific responses without destroying your cache hit ratio This is great if... ● You have a response that is tailored to different device types ● There are a virtually infinite number of User-Agent values 2
UA Targeting 17 /normalizeUA /polyfill.js?ua=ie/11 /polyfill.js Add the normalised User- Agent to the URL and restart the original request Add a Vary: User-Agent header to the response before sending it back to the browser We call this a preflight request
UA targeting: key tools and techniques ● Remember something using request headers: set req.http.tmpOrigURL = req.url; ● Change the URL of the backend request: set req.url = "/api/normalizeUA?ua=" req.http.User-Agent; ● Reconstruct original URL adding a backend response header: set req.url = req.http.tmpOrigURL "?ua=" resp.http.NormUA; ● Restart to send the request back to vcl_recv: restart; 18
Authentication Implement integration with your federated identity system entirely in VCL This is great if... ● You have a federated login system using a protocol like OAuth ● You want to annotate requests with a simple verified authentication state 3
Authentication: key tools and techniques ● Get a cookie by name: req.http.Cookie:MySiteAuth ● Base64 normalisation: digest.base64url_decode(), digest.base64_decode ● Extract the parts of a JSON Web Token (JWT): regsub({{cookie}}, "(^[^\.]+)\.[^\.]+\.[^\.]+$", "\1"); ● Check JWT signature: digest.hmac_sha256_base64() ● Set trusted headers for backend use: req.http.Nikkei-UserID = regsub({{jwt}}, {{pattern}}, "\1"); 24
Feature flags Dark deployments and easy A/B testing without reducing front end perf or cache efficiency This is great if... ● You want to serve different versions of your site to different users ● Test new features internally on prod before releasing them to the world 4
Feature flags parts 30 ● A flags registry - a JSON file will be fine ○ Include all possible values of each flag and what percentage of the audience it applies to ○ Publish it statically - S3 is good for that ● A flag toggler tool ○ Reads the JSON, renders a table, writes an override cookie with chosen values ● An API ○ Reads the JSON, responds to requests by calculating a user's position number on a 0-100 line and matches them with appropriate flag values ● VCL ○ Merges flag data into requests
ExpressJS flags middleware 32 app.get('/', (req, res) => { if (req.flags.has('highlights')) { // Enable highlights feature } }); HTTP/1.1 200 OK Vary: Nikkei-Flags ... Middleware provides convenient interface to flags header Invoking the middleware on a request automatically applies a Vary header to the response
Dynamic backends Override backend rules at runtime without updating your VCL This is great if... ● You have a bug you can't reproduce without the request going through the CDN ● You want to test a local dev version of a service with live integrations 5
Dynamic backends 34 Developer laptop Dynamic backend proxy (node-http-proxy) Check forwarded IP is whitelisted or auth header is also present GET /article/123 Backend-Override: article -> fc57848a.ngrok.io Detect override header, if path would normally be routed to article, change it to override proxy instead. ngrok fc57848a .ngrok.io Normal production backends
Dynamic backends: key tools and techniques ● Extract backend to override: set req.http.tmpORBackend = regsub(req.http.Backend-Override, "\s*\-\>.*$", ""); ● Check whether current backend matches if (req.http.tmpORBackend == req.http.tmpCurrentBackend) { ● Use node-http-proxy for the proxy app ○ Remember res.setHeader('Vary', 'Backend-Override'); ○ I use {xfwd: false, changeOrigin: true, hostRewrite: true} 35
Debug headers Collect request lifecycle information in a single HTTP response header This is great if... ● You find it hard to understand what path the request is taking through your VCL ● You have restarts in your VCL and need to see all the individual backend requests, not just the last one 6
RUM++ Resource Timing API + data Fastly exposes in VCL. And no backend. This is great if... ● You want to track down hotspots of slow response times ● You'd like to understand how successfully end users are being matched to their nearest PoPs 7
Resource timing on front end 43 var rec = window.performance.getEntriesByType("resource") .find(rec => rec.name.indexOf('[URL]') !== -1) ; (new Image()).src = '/sendBeacon'+ '?dns='+(rec.domainLookupEnd-rec.domainLookupStart)+ '&connect='+(rec.connectEnd-rec.connectStart)+ '&req='+(rec.responseStart-rec.requestStart)+ '&resp='+(rec.responseEnd-rec.responseStart) ;
Add CDN data in VCL & respond with synthetic 44 sub vcl_recv { if (req.url ~ "^/sendBeacon") { error 700 "GIF"; } } sub vcl_error { if (obj.status == 700) { set obj.status = 200; set obj.response = "OK"; set obj.http.Content-Type = "image/gif"; synthetic digest.base64_decode("R0lGODlhAQABAIAAAA..."); return (deliver); } }
Beyond ASCII Use these encoding tips to embed non-ASCII content in your VCL file. This is great if... ● Your users don't speak English, but you can only write ASCII in VCL files 8
Varnishlog to the rescue A way to submit a varnish transaction ID to the API, and get all varnishlog events relating to that transaction, including related (backend) transactions 55 > fastly log 1467852934 17 SessionOpen c 66.249.72.22 47013 :80 17 ReqStart c 66.249.72.22 47013 1467852934 17 RxRequest c GET 17 RxURL c /articles/123 17 RxProtocol c HTTP/1.1 17 RxHeader c Host: www.example.com ...