Madison PHP Conference
September 13th, 2014
© David Buchmann, Liip AG
1 | apt-get install varnish |
/etc/default/varnish
, replace port 6081 by port 801 | service apache2 restart |
2 | service varnish restart |
1 | GET /path |
2 |
3 | HTTP/1.1 200 OK |
4 | Content-Type: text/html |
5 |
6 | <html>...</html> |
https://twitter.com/stevelosh/status/372740571749572610
http://httpstatusdogs.com
1 | Cache-Control: s-maxage=3600, max-age=900 |
2 | Expires: Thu, 15 May 2014 08:00:00 GMT |
1 | Cache-Control: s-maxage=0, private, no-cache |
1 | Accept: application/json |
1 | Vary: Accept |
1 | ETag: 82901821233 |
1 | If-None-Match: 82901821233 |
2 |
3 | 304 Not Modified |
1 | Last-Modified: Tue, 13 May 2014 08:13:20 GMT |
1 | If-Modified-Since: Tue, 13 May 2014 08:13:20 GMT |
2 |
3 | 304 Not Modified |
wget -Sq --spider http://localhost/path.html curl -o /dev/null -sD - http://cmf.lo/app_dev.php
1 | // setting headers is no longer possible |
2 | // when output started! |
3 | header( 'Cache-Control: s-maxage=600, max-age=60' ); |
4 | header( 'Etag: ' . sha1( $data )); |
1 | // DefaultController::indexAction |
2 | $response = $this ->render( '::index.html.twig' ); |
3 | $response ->setSharedMaxAge(600); |
4 | $response ->setMaxAge(60); |
5 | $response ->setEtag(sha1( $response ->getContent())); |
6 |
7 | return $response ; |
1 | # ... |
2 | # Cache the homepage for 10 minutes |
3 | - |
4 | match: |
5 | path: ^/$ |
6 | headers: |
7 | cache_control: |
8 | public: true |
9 | max_age: 600 |
vcl_recv: entry point, lookup or pass vcl_fetch: receive from backend, cache or not vcl_deliver: remove headers not for client vcl_hash: determine cache key vcl_hit: found in cache, deliver or pass vcl_miss: pass or fetch vcl_pipe: do not alter request vcl_error: define error page
Think carefully and test thoroughly
1 | sub vcl_deliver { |
2 | if (obj.hits > 0) { |
3 | set resp.http.X-Cache = "HIT"; |
4 | } else { |
5 | set resp.http.X-Cache = "MISS"; |
6 | } |
7 | } |
1 | sub vcl_recv { |
2 | if (req.http.Cookie) { |
3 | if (req.url ~ "^/static") { |
4 | remove req.http.Cookie; |
5 | } |
6 | } |
7 | } |
01 | sub vcl_recv { |
02 | if (req.http.Cookie) { |
03 | # removes all cookies named __utm? (utma, ...) |
04 | set req.http.Cookie = regsuball( |
05 | req.http.Cookie, |
06 | "(^|; ) *__utm.=[^;]+;? *", "\1" |
07 | ); |
08 |
09 | if (req.http.Cookie == "") { |
10 | remove req.http.Cookie; |
11 | } |
12 | } |
1 | < link rel = "stylesheet" href = "/css/style.css?v1" type = "text/css" /> |
2 | ... |
3 | < script src = "/js/scripts.js?v1" ></ script > |
Built-in support for cache busting with Symfony & Assetic:
1 | # app/config.yml |
2 | framework: |
3 | templating: |
4 | assets_version: v1 |
01 | acl invalidators { |
02 | "localhost"; |
03 | } |
04 |
05 | sub vcl_recv { |
06 | if (req.request == "PURGE") { |
07 | if (!client.ip ~ invalidators) { |
08 | error 405 "PURGE not allowed"; |
09 | } |
10 | return (lookup); |
11 | } |
12 | } |
13 | ... |
01 | ... |
02 | sub vcl_hit { |
03 | if (req.request == "PURGE") { |
04 | purge; |
05 | error 200 "Purged"; |
06 | } |
07 | } |
08 | sub vcl_miss { |
09 | if (req.request == "PURGE") { |
10 | purge; |
11 | error 200 "Purged (not found)"; |
12 | } |
13 | } |
1 | $cacheInvalidator ->invalidatePath( '/my/path' ); |
2 | ... |
3 | $cacheInvalidator -> flush (); |
01 | // CommentsController::postAction |
02 | // page changed, send purge request for this url |
03 | ... |
04 | $cacheManager = $container ->get( |
05 | 'fos_http_cache.cache_manager' |
06 | ); |
07 | $cacheManager ->invalidateRoute( 'page' , array ( |
08 | 'id' => $post ->getPage()->getId() |
09 | ); |
10 | ... |
1 | # app/config.yml |
2 |
3 | fos_http_cache: |
4 | proxy_client: |
5 | varnish: |
6 | servers: 4.4.4.11:80, 4.4.4.22:80 |
7 | base_url: yourwebsite.com |
Group based caching
1 | X-Cache-Tags: comment1, comment2 |
1 | ban("obj.http.x-cache-tags ~ " + |
2 | req.http.x-cache-tags |
3 | ); |
1 | /** @var $cm CacheManageer */ |
2 | $cm ->tagResponse( $response , array ( 'comment-42' )); |
3 | ... |
4 | $cm ->invalidateTags( array ( 'comment-42' )); |
01 | use FOS\HttpCacheBundle\Configuration\Tag; |
02 |
03 | class CommentController extends Controller |
04 | { |
05 | /** |
06 | * @Tag({"comments", "'comment-'~id"}) |
07 | */ |
08 | public function commentAction( $id ) |
09 | { |
10 | // ... |
1 | < html > |
2 | < body > |
3 | Main body. |
4 | < esi:include src = "/esi-fragment.php" /> |
5 | </ body > |
6 | </ html > |
-p esi_syntax=0x00000003
01 | sub vcl_recv { |
02 | // Announce ESI support to backend. |
03 | set req.http.Surrogate-Capability = "abc=ESI/1.0"; |
04 | } |
05 |
06 | sub vcl_fetch { |
07 | // Check for ESI acknowledgement |
08 | if (beresp.http.Surrogate-Control ~ "ESI/1.0") { |
09 | unset beresp.http.Surrogate-Control; |
10 | set beresp.do_esi = true; |
11 | } |
12 | } |
1 | # app/config/config.yml |
2 | framework: |
3 | esi: { enabled: true } |
4 | fragments: { path: /_fragment } |
1 | {# index.html.twig #} |
2 | {% render_esi(controller( 'DbuCoreBundle:Comments:comments', {'param': 42 })) %} |