HTTP	caching
with	Varnish
DPC,	June	27th	2015
David	Buchmann	&	David	de	Boer
This	thing	called	Varnish
# apt-get install varnish
Edit	/etc/default/varnish
Replace	port	6081	with	80
# service varnish restart
Webserver	on	port	8080
# service apache2 restart
What	could	possibly	go
wrong?
Nothing	gets	cached
Too	much	gets	cached
Editors	see	no	changes
Caches	get	mixed	up
Let’s	take	a	step	back
HTTP	is	simple
GET /path
HTTP/1.1 200 OK
Content-Type: text/html
...
Default	behaviour
Only	GET	and	HEAD	are	cached
Cache	for	2	minutes
Requests	with	Cookie/Authorization	are	not	cached
Responses	with	Set-Cookie	are	not	cached
Cache	control	headers
Cache-Control: s-maxage=3600, max-age=900
Expires: Sat, 27 June 2015 09:58:00 GMT
Cache	control	headers
Set	them	in	your	app,	not	in	Varnish
Cache-Control: s-maxage=3600, max-age=900
Expires: Sat, 27 June 2015 09:58:00 GMT
Do	not	cache
Cache-Control: s-maxage=0, private, no-cache
Cache	validation
ETag
Unique	resource	fingerprint
Client	sends	ETag
Still	the	same?	304	Not-Modified
Prefer	ETag	over	Last-Modified
curl -o /dev/null -sD - http://fos.lo/path
curl -o /dev/null -sD - http://fos.lo/path
HTTP/1.1 200 OK
Date: Fri, 22 Jun 2015 19:23:02 GMT
Cache-Control: s-maxage=30
Vary: Accept-Encoding
Content-Type: application/json
Age: 23
ETag: 4423466221248
Plain	PHP
header('Cache-Control: s-maxage=600, max-age=60');
header('Etag: ' . sha1($data));
Plain	PHP
Do	this	before	any	output
is	sent	to	the	client
header('Cache-Control: s-maxage=600, max-age=60');
header('Etag: ' . sha1($data));
Symfony
// DefaultController::indexAction
$response = $this->render('::index.html.twig');
$response->setSharedMaxAge(600);
$response->setMaxAge(60);
$response->setEtag(sha1($response->getContent()));
return $response;
Symfony
Add	above	or	annotation	to	every	action
// DefaultController::indexAction
$response = $this->render('::index.html.twig');
$response->setSharedMaxAge(600);
$response->setMaxAge(60);
$response->setEtag(sha1($response->getContent()));
return $response;
FOSHttpCacheBundle
app/config/config.yml:
match:
path: "^/articles"
headers:
cache_control:
s_maxage: 600
max_age: 60
etag: true
Overview
VCL
Invalidation
Tagging
VCL
Cookies
sub vcl_recv {
if (req.http.Authorization || req.http.Cookie) {
return (pass);
}
return (hash);
}
Remove	cookies
Remove	cookies
sub vcl_recv {
if (req.http.Cookie) {
if (req.url ~ "^/static") {
unset req.http.Cookie;
}
}
}
Remove	cookies
Adds	to	default	behaviour
sub vcl_recv {
if (req.http.Cookie) {
if (req.url ~ "^/static") {
unset req.http.Cookie;
}
}
}
Invalidation
There	are	only	two	hard	things	in	computer	science:
Cache	invalidation
Naming	things
Off	by	one	errors
Invalidation
Actively	remove	response	from	cache
Long	TTL
More	hits
Invalidation
Actively	remove	response	from	cache
Long	TTL
More	hits
Remember:	bust	your	assets!
Invalidation	flavours
Purge:	URL	and	variants
Refresh:	exact	request,	warm	cache
Ban:	batch	with	regular	expressions
Invalidation	flavours
Purge:	URL	and	variants
Ban:	batch	with	regular	expressions
Purge
Configure	Varnish
acl invalidators {
"localhost";
}
vcl_recv {
if (req.method == "PURGE") {
if (!client.ip ~ invalidators) {
return (synth(405, "Not allowed"));
}
return (purge);
}
}
FOSHttpCache
$cacheInvalidator->invalidatePath('/my/path');
...
$cacheInvalidator->flush();
Ban
Ban	is	powerful
Regular	expressions
URL
And	any	request	header
Custom	Varnish	configuration
Cache	taggingCache	tagging
Set	tags
When	comment-1	changes:
BAN	request
for	X-Cache-Tags
that	match	on:	comment-1(,.+)?
X-Cache-Tags: comment-1, comment-2
Invalidate	tags
sub vcl_recv {
# ...
ban("obj.http.x-cache-tags ~ " +
req.http.x-cache-tags
);
# ...
}
FOSHttpCacheBundle
$tagHandler->addTag(['comment-1', 'comment-2']);
$tagHandler->invalidateTags(['comment-1']);
use FOSHttpCacheBundleConfigurationTag;
class CommentController extends Controller
{
/**
* @Tag({"comments", "'comment-'~id"})
*/
public function commentAction($id)
{
Wrap-up
Increase	your	hitsIncrease	your	hits
Remove	cookies	where	possible
Cache	authenticated	content
Increase	TTL
Inspect	cache	misses:
varnishtop -i BereqURL
FOSHttpCache
Well-tested	Varnish	integration	for	PHP	apps
Provides	testing	tools
Cache	authenticated	content	by	group
Also	for	Nginx	&	Symfony	HttpCache
Extensive	documentation
Symfony	bundle
Version	2.0:	PSR-7,	VCL	functions
KISS	VCL
Application	performance
Frontend	performance
Questions,	input,	feedback?
Twitter:
@dbu		@ddeboer_nl
https://joind.in/14240
Varnish	4
Client/backend	separation
Simpler	VCL
Finally	takes	Cache-Control	seriously
User	context	caching
Cache	authenticated	responses
Cache	for	groups	of	users
Let	backend	decide	on	groups
ESI
<html>
<body>
Main body.
<esi:include src="/esi-fragment.php" />
</body>
</html>
Content-negotation
Need	to	keep	variants	apart
Request:
Response:
Accept: application/json
Vary: Accept

HTTP caching with Varnish