Fast Caching frontend
This is were Varnish-cache comes into place.
Varnish is a well known HTTP accelerator and a fabulous caching system. It is known to be widely used by Facebook, Paas Platform Heroku and many others.
However, Varnish has many drawbacks, including :
- Full configuration is very complex and should be totally adapted to the code of the web application
- It is quite impossible to make a single Varnish instance work with more than one webapp code (No WordPress + Magento on the same frontend)
- An error in your configuration file could kill your performance or even worse makes you cache logged user traffic which could be terrible if done on an eCommerce software
However, Varnish has a well documented and extendable configuration (can even take Inline C!), is really easy to implement with WordPress, and will make our WordPress just fly.
I’ll pass on explaining the whole varnish configuration, as some is very complex. I’ll do a special paper about Varnish configuration. In the meantime, you can have a look here, here and there.
So first install varnish :
apt-get install varnish
Then edit the starting options, bind it to the port 80 and make some subsequent adjustment. Still on Debian, /etc/default/varnish.
DAEMON_OPTS="-a :80 \
-T localhost:6082 \
-f /etc/varnish/default.vcl \
-S /etc/varnish/secret \
-s file,/var/lib/varnish/$INSTANCE/varnish_storage.bin,1G \
-p thread_pools=1 \
-p thread_pool_min=100 \
-p thread_pool_max=2000 \
-p thread_pool_add_delay=2 \
-p cli_timeout=10 \
-p session_linger=100 \
-p lru_interval=20"
Then there is the configuration file I specialy crafted for WordPress. Edit /etc/varnish/default.vcl
backend local {
#Make it through nginx ( Internet ===> Varnish:80 ===> Nginx:8081 ===> Apache:8080 )
.host = "127.0.0.1";
.port = "8081";
}
sub vcl_recv {
# Rules for all requests
# Only the blog URL is cached
if (req.http.host ~ "(blog\.brigato\.fr)") { set req.backend = local; }
else { return (pass); }
# Compatiblity with Apache log
remove req.http.X-Forwarded-For;
set req.http.X-Forwarded-For = client.ip;
# Post requests will not be cached
if (req.request == "POST") {
return (pass);
}
# Normalize encoding/compression
if (req.http.Accept-Encoding) {
if (req.http.Accept-Encoding ~ "gzip") { set req.http.Accept-Encoding = "gzip"; }
elsif (req.http.Accept-Encoding ~ "deflate") { set req.http.Accept-Encoding = "deflate"; }
else { remove req.http.Accept-Encoding; }
}
# Remove has_js and Google Analytics __* cookies.
if (req.http.cookie) {
set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(__[a-z]+|has_js)=[^;]*", "");
# Remove a ";" prefix, if present.
set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", "");
# Remove empty cookies.
if (req.http.Cookie ~ "^\s*$") {
unset req.http.Cookie;
}
}
# Serve the page
unset req.http.vary;
# If I am logged in to wordpress, I DO NOT WANT TO SEE cached pages
if ( req.url ~ "^/wp-(login|admin)" || req.http.Cookie ~ "wordpress_logged_in_" ) {
return (pass);
} else {
# If I'm just a regular visitor
# If the request is static
if (req.url ~ "\.(jpeg|jpg|png|gif|ico|js|css|txt|gz|zip|lzma|bz2|tgz|tbz|html|htm)$") {
# Remove the cookie and make the request static
unset req.http.cookie;
return (lookup);
}
# Try to lookup in the cache
return (lookup);
}
# Cookie ? Not cacheable by default
if (req.http.Authorization || req.http.Cookie) {
return (pass);
}
# if host header is empty return 404
error 404 req.http.host;
return (lookup);
}
sub vcl_fetch {
if (req.http.host ~ "(blog\.brigato\.fr)") {
# Do not cache POST requests
if (req.request == "POST") {
return (pass);
}
# If the request is static
if (req.url ~ "\.(jpeg|jpg|png|gif|ico|js|css|txt|gz|zip|lzma|bz2|tgz|tbz|html|htm)$") {
# Cache it, and make it last 2 hours
set beresp.ttl = 7200s;
# Make the request static by removing any cookies set by those static files
unset beresp.http.set-cookie;
# Deliver the cached object
return (deliver);
}
# If I am logged in to wordpress, I DO NOT WANT TO SEE cached pages
if (req.http.cookie ~ "wordpress_logged_in") {
return (pass);
} else {
# Cache anything for 2 minutes. When the cache expires it will be cached again and again, at the time of the request
set beresp.ttl = 120s;
return (deliver);
}
}
}
sub vcl_deliver {
# Secure the header
remove resp.http.Via;
remove resp.http.X-Varnish;
remove resp.http.Server;
remove resp.http.X-Powered-By;
}
Remember to set theses lines according to your needs :
backend local {
#Make it through nginx ( Internet ===> Varnish:80 ===> Nginx:8081 ===> Apache:8080 )
.host = "127.0.0.1";
.port = "8081";
}
[...]
# Rules for all requests
# Only the blog URL is cached
if (req.http.host ~ "(blog\.brigato\.fr)") { set req.backend = local; }
[...]
sub vcl_fetch {
if (req.http.host ~ "(blog\.brigato\.fr)") {
Then restart the varnish cache frontend.
/etc/init.d/varnish restart
At this time, you should obtain a Huge boost on performance. For me, it was 2700 requests/sec, as in my last post.
However this is still not good, as I knew thanks to further tests that Varnish was able to push more than 5000 requests/sec for static cached content.
Here is the trick : Remember to active KeepAlive Requests in Apache frontend. This was not activated under my Debian Installation.
Edit /etc/apache2/apache2.conf, look for KeepAlive lines, and adjust them :
KeepAlive On MaxKeepAliveRequests 100 KeepAliveTimeout 15
Then restart apache a last time.
/etc/init.d/apache2 restart
I’m using Varnish too now, and I like the 1000-2000 hits/s that it can easily provide on my low-end VPS. I do find you need quite a lot of RAM for it to work well, though!
Pingback: WordPress Optimization Tips - Gabfire Premium WordPress Themes