Bulgaria PHP Conference, September 25th, 2015
apt-get install varnish
apt-get install varnish
/etc/default/varnish, replace port 6081 by port 80
service apache2 restart
service varnish restart
httpstatusdogs.com
GET /path
HTTP/1.1 200 OK
Content-Type: text/html
<html>...</html>
twitter.com/stevelosh/status/372740571749572610
wget -Sq --spider varnish.lo/solutions/expiration.php
curl -o /dev/null -sD - varnish.lo/solutions/expiration.php
HTTP 1.1, RFC 2616, Sections 13.2 and 13.3
Cache-Control: s-maxage=3600, max-age=900
Expires: Thu, 15 May 2014 08:00:00 GMT
curl -sD - varnish.lo/noinfo.php
curl -sD - varnish.lo/exercises/expiration.php
ETag: 82901821233
If-None-Match: 82901821233
304 Not Modified
curl -sD - varnish.lo/exercises/etag.php
curl -H "If-None-Match: abc" -sD - varnish.lo/exercises/etag.php
Last-Modified: Tue, 13 May 2014 08:13:20 GMT
If-Modified-Since: Tue, 13 May 2014 08:13:20 GMT
304 Not Modified
curl -sD - varnish.lo/exercises/last-modified.php
curl -H "If-Modified-Since: {previous timestamp}" -sD - varnish.lo/exercises/last-modified.php
gmdate('r') does not work as timezone is output as "+0000" instead of "GMT".
Cache-Control: s-maxage=0, private, no-cache
www.varnish-cache.org/trac/browser/bin/varnishd/default.vcl?rev=3.0 (Varnish 3)
www.varnish-cache.org/trac/browser/bin/varnishd/builtin.vcl?rev=4.0 (Varnish 4)
curl -sD - varnish.lo/exercises/nocache.php
curl -sD - varnish.lo/cookie.php
curl -H "Cookie: x" -sD - varnish.lo/solutions/expiration.php
GET /resource
Accept: application/json
GET /resource
Accept: text/xml
Vary: Accept
curl -sD - varnish.lo/exercises/content-negotiation.php
curl -H "Accept: application/json" -sD - varnish.lo/exercises/content-negotiation.php
Think carefully and test thoroughly
sub vcl_backend_response {
set beresp.http.X-TTL = beresp.ttl;
}
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT";
} else {
set resp.http.X-Cache = "MISS";
}
}
backend default {
.host = "127.0.0.1";
.port = "8080";
}
backend legacy {
.host = "127.0.0.1";
.port = "8000";
}
sub vcl_recv {
if (req.url ~ "^/archive/") {
set req.backend_hint = legacy;
} else {
set req.backend_hint = default;
}
}
sub vcl_recv {
if (req.http.Cookie) {
if (req.url ~ "^/cache") {
remove req.http.Cookie;
}
}
}
curl -H "Cookie: x" -sD - varnish.lo/solutions/expiration.php
header('X-Reverse-Proxy-TTL: 30');
C{ #include <stdlib.h>; }C
sub vcl_backend_response {
if (beresp.http.X-Reverse-Proxy-TTL) {
C{
char *ttl;
const struct gethdr_s hdr = { HDR_BERESP, "\024X-Reverse-Proxy-TTL:" };
ttl = VRT_GetHdr(ctx, &hdr);
VRT_l_beresp_ttl(ctx, atoi(ttl));
}C
unset beresp.http.X-Reverse-Proxy-TTL;
}
}
...
# config/varnish/common.vcl
include "custom-ttl.vcl";
sudo service varnish restart
curl -sD - varnish.lo/exercises/custom-ttl.php
Average number of requests over last 60 seconds
varnishtop
Top URLs that are missing the cache:
varnishtop -i BereqUrl
Client or backend only:
varnishtop -c
varnishtop -b
Show header values:
varnishtop -I ReqHeader:Cookie
Log all the things:
varnishlog
Again, only client or backend:
varnishlog -c
varnishlog -b
Log entries for specific URL:
varnishlog -q 'ReqUrl ~ "/exercises"'
varnishlog -q 'BereqUrl ~ "/exercises"'
<< Request >> 32770
- Begin req 32769 rxreq
- Timestamp Start: 1433683498.334761 0.000000 0.000000
- Timestamp Req: 1433683498.334761 0.000000 0.000000
- ReqStart 127.0.0.1 45447
- ReqMethod PURGE
- ReqURL /cache.php
- ReqProtocol HTTP/1.1
- ReqHeader User-Agent: GuzzleHttp/6.0.1 curl/7.38.0 PHP/5.6.7-1
- ReqHeader Host: localhost:6181
- ReqHeader X-Forwarded-For: 127.0.0.1
- VCL_call RECV
- VCL_acl MATCH invalidators "127.0.0.1"
- VCL_return purge
- VCL_call HASH
- VCL_return lookup
- VCL_call PURGE
- Debug "VCL_error(201, PURGED!)"
- VCL_return synth
- Timestamp Process: 1433683498.334931 0.000170 0.000170
- RespHeader Date: Sun, 07 Jun 2015 13:24:58 GMT
- RespHeader Server: Varnish
- RespHeader X-Varnish: 32770
- RespProtocol HTTP/1.1
- RespStatus 201
- RespReason Created
- RespReason PURGED!
- VCL_call SYNTH
- RespHeader Content-Type: text/html; charset=utf-8
- RespHeader Retry-After: 5
- VCL_return deliver
- RespHeader Content-Length: 243
- Debug "RES_MODE 2"
- RespHeader Connection: keep-alive
- Timestamp Resp: 1433683498.334985 0.000224 0.000054
- ReqAcct 105 0 105 197 243 440
varnishlog and varnishtop
curl -sD - varnish.lo/solutions/content-negotiation.php
curl -H "Accept: application/json" -sD - varnish.lo/solutions/content-negotiation.php
Accept header.
varnishtop -I ReqHeader:'Accept:'
varnishlog -I ReqHeader:'Accept:'
vendor/friendsofsymfony/http-cache/tests/FunctionalRun the tests:
vendor/bin/phpunit
Edit the test:
tests/CacheTest.php
Edit the code:
web/exercises/post.php
Make it green!
<link rel="stylesheet" href="/css/style.css?v1" type="text/css"/>
...
<script src="/js/scripts.js?v1"></script>
acl invalidators {
"localhost";
}
if (req.method == "PURGE") {
if (!client.ip ~ invalidators) {
return (synth(405, "Not allowed"));
}
return (purge);
}
...
# config/varnish/common.vcl
include "invalidation.vcl";
sudo service varnish restart
curl -sD - varnish.lo/solutions/expiration.php
curl -X PURGE -sD - varnish.lo/solutions/expiration.php
curl -sD - varnish.lo/solutions/expiration.php
curl -sD - varnish.lo/invalidation/
curl -X POST --data 'method=Purge' -sD - varnish.lo/invalidation/update.php
curl -sD - varnish.lo/invalidation/
curl -X POST --data 'method=Refresh' -sD - varnish.lo/invalidation/update.php
curl -sD - varnish.lo/invalidation/
curl -sD - varnish.lo/invalidation/?x
curl -X POST --data 'method=Purge' -sD - varnish.lo/invalidation/update.php
curl -sD - varnish.lo/invalidation/?x
curl -X POST --data 'method=Ban' -sD - varnish.lo/invalidation/update.php
curl -sD - varnish.lo/invalidation/?x
ban("obj.http.x-cache-tags ~ "
+ req.http.x-cache-tags
);
curl -sD - varnish.lo/exercises/tagging/?filter=apple
curl -sD - varnish.lo/exercises/tagging/?filter=orange
curl -X POST --data 'applepie=New description' -sD - varnish.lo/exercises/tagging/update.php
curl -sD - varnish.lo/exercises/tagging/?filter=apple
curl -sD - varnish.lo/exercises/tagging/?filter=orange
/** @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)
// ...
<html>
<body>
Main body.
<esi:include src="fragment.php" />
</body>
</html>
-p esi_syntax=0x00000003
sub vcl_recv {
// Add a Surrogate-Capability header
// to announce ESI support.
set req.http.Surrogate-Capability = "abc=ESI/1.0";
}
sub vcl_backend_response {
// Check for ESI acknowledgement
// and remove Surrogate-Control header.
if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
unset beresp.http.Surrogate-Control;
set beresp.do_esi = true;
}
}
# config/varnish/common.vcl
include "esi.vcl";
sudo service varnish restart
curl -sD - varnish.lo/exercises/esi/
curl -sD - varnish.lo:8080/exercises/esi/
curl -sD - varnish.lo/exercises/esi/fragment.php
# app/config/config.yml
framework:
esi: { enabled: true }
fragments: { path: /_fragment }
Make sure either your webserver is only reachable from the Varnish server or add access restrictions for /_fragment
{# index.html.twig #}
{% render_esi(controller( 'AppBundle:Comments:comments', {'param': 42 })) %}
sub vcl_recv {
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 == "") {
remove req.http.Cookie;
}
}
varnish-cache.org/trac/wiki/VCLExampleRemovingSomeCookies #RemovingallBUTsomecookies
if (isset($_COOKIE[session_name()])) {
setcookie(session_name(), '', time()-3600);
}
varnish.lo/limit-session/ (with your browser)
Group based caching
# config/varnish/common.vcl
include "custom-ttl.vcl";
include "user-context.vcl";
sudo service varnish restart
varnish.lo/user-context/ (with your browser)