Insights

Symfony2 Caching To Improve Speed

Varnish is an open source HTTP accelerator that is powerful enough to be capable of serving cached content quickly. It also includes support for Edge Side Includes that allows certain parts of the pages to have a different caching strategy than the main page. The good part about Symfony 2 is that it’s cache uses standard HTTP cache headers and the Symfony 2 Reverse Proxy can easily be replaced with any other reverse proxy. Symfony 2 development offers that much flexibility for you to improve and optimize speed.

Here’s how you can configure it:

Symfony 2 is smart and can detect whether the reverse proxy understands ESI (Edge Side Includes) or not. With Symfony 2 reverse proxy, it works out of the box. owever, getting to the point where you need to get Symfony 2 work with Varnish, certain special configuration are required. Here’s how you get that done:
NOTE that Symfony 2 uses only src attribute for ESI tags.
The first step would be to add a surrogate-capability header to requests forwarded to the backend application. This would configure varnish to advertise it’s ESI support.

sub vcl_recv {
// Add a Surrogate-Capability header to announce ESI support.
set req.http.Surrogate-Capability = "abc=ESI/1.0";
}

The next step would be to optimize varnish. This is so that it parses only response contents when there is at least one ESI tag by checking the Surrogate-Control header. This header is added automatically by that Symfony2.

sub vcl_fetch {
/*
Check for ESI acknowledgement
and remove Surrogate-Control header
*/
if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
unset beresp.http.Surrogate-Control;
// For Varnish >= 3.0
set beresp.do_esi = true;
// For Varnish < 3.0
// esi;
}
/* By default Varnish ignores Cache-Control: nocache
(https://www.varnish-cache.org/docs/3.0/tutorial/increasing_your_hitrate.html#cache-control),
so in order avoid caching it has to be done explicitly */
if (beresp.http.Pragma ~ "no-cache" ||
beresp.http.Cache-Control ~ "no-cache" ||
beresp.http.Cache-Control ~ "private") {
return (hit_for_pass);
}
}

NOTE: Compression is not supported with the earlier versions of Varnish. (Before 3.0). However, it can be configured with a web server in front for varnish.

Cache Invalidation

Invalidation of cache is already taken into account by the HTTP cache models. So, mostly, you would not need to worry about Invalidation.
Varnish can be configured to accept HTTP PURGE method that will help invalidation of cache for a given resource.

/*
Connect to the backend server
on the local machine on port 8080
*/
backend default {
.host = "127.0.0.1";
.port = "8080";
}
sub vcl_recv {
/*
Varnish default behavior doesn't support PURGE.
Match the PURGE request and immediately do a cache lookup,
otherwise Varnish will directly pipe the request to the backend
and bypass the cache
*/
if (req.request == "PURGE") {
return(lookup);
}
}
sub vcl_hit {
// Match PURGE request
if (req.request == "PURGE") {
// Force object expiration for Varnish < 3.0 set obj.ttl = 0s; // Do an actual purge for Varnish >= 3.0
// purge;
error 200 "Purged";
}
}
sub vcl_miss {
/*
Match the PURGE request and
indicate the request wasn't stored in cache.
*/
if (req.request == "PURGE") {
error 404 "Not purged";
}
}

You will need to create an access list for protecting the PURGE HTTP method to avoid random people from gaining access to your cached data.

Here’s how:

/*
Connect to the backend server
on the local machine on port 8080
*/
backend default {
.host = "127.0.0.1";
.port = "8080";

// ACL's can contain IP's, subnets and hostnames
acl purge {
"localhost";
"192.168.55.0"/24;
}
sub vcl_recv {
// Match PURGE request to avoid cache bypassing
if (req.request == "PURGE") {
// Match client IP to the ACL
if (!client.ip ~ purge) {
// Deny access
error 405 "Not allowed.";
}
// Perform a cache lookup
return(lookup);
}
}
sub vcl_hit {
// Match PURGE request
if (req.request == "PURGE") {
// Force object expiration for Varnish < 3.0 set obj.ttl = 0s; // Do an actual purge for Varnish >= 3.0
// purge;
error 200 "Purged";
}
}
sub vcl_miss {
// Match PURGE request
if (req.request == "PURGE") {
// Indicate that the object isn't stored in cache
error 404 "Not purged";
}
}

Routing and X-FORWARDED Headers

X-FORWARDED headers must be added to Symfony so that it generates the correct URLs with Varnish and that Symfony is aware of the original port number of the request. For example, if Varnish and webserver are hosted on a single server, and Varnish is listening on port 80, and Apache is listening on port 8080, You should add the X-Forwarded-Port header so that Symfony understands that the original port is 80 and not 8080.

If the header is not added correctly, Symfony will incorrectly append 8080 while generating URLs.

sub vcl_recv {
if (req.http.X-Forwarded-Proto == "https" ) {
set req.http.X-Forwarded-Port = "443";
} else {
set req.http.X-Forwarded-Port = "80";
}
}

NOTE: It is essential to configure framework.trusted_proxies in the Symfony configuration so that Varnish is “seen” as the trusted proxy and X-Forwarded headers will be used.

Banner
In search for strategic sessions?
Let us understand your business thoroughly and help you strategies your digital product..
Book a session