SFUGZH, May 14th 2014
© David Buchmann, Liip AG
apt-get update apt-get install varnish
/etc/default/varnish
, replace port 6081 by port 80service apache2 restart service varnish restart
GET /path HTTP/1.1 200 OK Content-Type: text/html <html>...</html>
https://twitter.com/stevelosh/status/372740571749572610
http://httpstatusdogs.com
Cache-Control: s-maxage=3600, max-age=900 Expires: Thu, 15 May 2014 08:00:00 GMT
Cache-Control: s-maxage=0, private, no-cache
Vary: Accept, Cookie
Last-Modified: Tue, 13 May 2014 08:13:20 GMT
If-Modified-Since: Tue, 13 May 2014 08:13:20 GMT 304 Not Modified
ETag: 82901821233
If-None-Match: 82901821233 304 Not Modified
wget -Sq --spider http://localhost/path.html curl -o /dev/null -sD - http://cmf.lo/app_dev.php
HTTP/1.1 200 OK Date: Wed, 12 May 2014 08:20:06 GMT Cache-Control: no-cache Vary: Accept-Language,Accept-Encoding Content-Type: text/html; charset=UTF-8
// DefaultController::indexAction $response = $this->render('::index.html.twig'); $response->setMaxAge(600); $response->setPublic(); return $response;
fos_http_cache: rules: # login must not be cached - match: path: ^/(login|login_check|logout) controls: private: true max_age: 0 # Cache the homepage for 10 minutes - match: path: ^/$ vary: Cookie controls: public: true max_age: 600
# ... # Cache everything else for 1 hour - match: path: ^/ vary: Cookie controls: public: true max_age: 3600Clear the Symfony cache whenever you change values here
vcl_recv: entry point, lookup or pass vcl_pass: request from backend 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
sub vcl_recv { if (req.http.Cookie) { if (req.url ~ "^/static") { remove req.http.Cookie; } } }
sub vcl_deliver { if (obj.hits > 0) { set resp.http.X-Cache = "HIT"; } else { set resp.http.X-Cache = "MISS"; } }
fos_http_cache: rules: #... reverse_proxy_ttl: 600
sub vcl_fetch { ... if (beresp.http.X-Reverse-Proxy-TTL) { C{ char *ttl; ttl = VRT_GetHdr(sp, HDR_BERESP, "\024X-Reverse-Proxy-TTL:"); VRT_l_beresp_ttl(sp, atoi(ttl)); }C unset beresp.http.X-Reverse-Proxy-TTL; } ...
# app/config.yml framework: templating: assets_version: v1
<link rel="stylesheet" href="/css/style.css?v1" type="text/css"/> ... <script src="/js/scripts.js?v1"></script>
// CommentsController::postAction // the page changed, send a purge request for this url ... $path = $this->generate('page', array( 'id' => $post->getPage()->getId()) ); $cacheManager = $container->get('fos_http_cache.cache_manager'); $cacheManager->invalidatePath($path); ...
# app/config.yml fos_http_cache: proxy_client: varnish: servers: 4.4.4.11:80, 4.4.4.22:80 base_url: yourwebsite.com
# default.vcl sub vcl_recv { if (req.request == "PURGE") { if (!client.ip ~ invalidators) { error 405 "PURGE not allowed"; } return (lookup); } } sub vcl_hit { if (req.request == "PURGE") { purge; error 200 "Purged"; } } sub vcl_miss { // same as hit
/** @var $cm CacheManageer */ $cm->tagResponse($response, array('comment-42')); ... $cm->invalidateTags(array('comment-42'));
use FOS\HttpCacheBundle\Configuration\Tag; class CommentController extends Controller { /** * @Tag({"comments", "'comment-'~id"}) */ public function commentAction($id) { // ...
ban("obj.http.host ~ " + req.http.x-host + " && obj.http.x-url ~ " + req.http.x-url + " && obj.http.content-type ~ " + req.http.x-content-type + " && obj.http.x-cache-tags ~ " + req.http.x-cache-tags );
# app/config/config.yml framework: esi: { enabled: true } # app/config/routing.yml _internal: resource: "@FrameworkBundle/Resources/config/routing/internal.xml" prefix: /_internalMake sure either your webserver is only reachable by Varnish or add access restrictions for /_internal
{# index.html.twig #} {% render 'DbuCoreBundle:Comments:comments' with {}, {'standalone': true} %}
fos_http_cache: rules: # do not apply rules to _internal - match: path: ^/_internal # no controls
// UserController::showBoxAction $response = $this->render( 'DbuCoreBundle:User:box.html.twig'); $response->setVary('Cookie', false); $response->setMaxAge(0); $response->setPrivate(); return $response;
// CommentsController::commentsAction $response = $this->render( 'DbuCoreBundle:Comments:comments.html.twig', array('comments' => $this->getComments())); $response->setMaxAge(3600); $response->setPublic(); return $response;
$cm = $container->get('fos_http_cache.cache_manager'); $kernel = $this->container->get('http_kernel'); $path = $kernel->generateInternalUri( 'DbuCoreBundle:Comments:comments' ); $cm->invalidatePath($path);
Note: generateInternalUri is helpful but does not check if controller exists
$ app/console cache:clear --env=prod --no-debug $ curl -H "Surrogate-Capability: abc=ESI/1.0" \ http://performance.lo/
... <esi:include src="/_internal/secure/DbuCoreBundle%3AUser%3AshowLogin/none.html" onerror="continue" /> ... <esi:include src="/_internal/secure/DbuCoreBundle%3AComments%3Acomments/none.html" onerror="continue" /> ...
sub vcl_recv { set req.http.Surrogate-Capability = "abc=ESI/1.0"; # try to lookup even if there is a cookie return (lookup); }
sub vcl_fetch { if (beresp.http.Surrogate-Control ~ "ESI/1.0") { unset beresp.http.Surrogate-Control; set beresp.do_esi = true; } if (beresp.http.Vary) { return (pass); } }
sub vcl_deliver { if (! req.url ~ ".*\.(css|js|png)(\?.*)?$") { set resp.http.Vary = "Cookie"; unset resp.http.Last-Modified; } }