Skip to content

Instantly share code, notes, and snippets.

@benclark
Created July 25, 2012 13:56
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save benclark/3176314 to your computer and use it in GitHub Desktop.
Save benclark/3176314 to your computer and use it in GitHub Desktop.
Varnish config file w/ mobile redirect
backend default {
.host = "127.0.0.1";
.port = "8080";
.connect_timeout = 600s;
.first_byte_timeout = 600s;
.between_bytes_timeout = 600s;
}
acl purge {
"localhost";
"127.0.0.1";
}
sub vcl_recv {
if (req.request == "GET" && req.url ~ "^/varnishcheck$") {
error 200 "Varnish is Ready";
}
# Add a unique header containing the client address.
set req.http.X-Forwarded-For = client.ip;
if (req.request != "GET" &&
req.request != "HEAD" &&
req.request != "PUT" &&
req.request != "POST" &&
req.request != "TRACE" &&
req.request != "OPTIONS" &&
req.request != "PURGE" &&
req.request != "DELETE") {
# Non-RFC2616 or CONNECT which is weird.
return (pipe);
}
# We only deal with GET, PURGE and HEAD by default.
if (req.request != "GET" && req.request != "HEAD" && req.request != "PURGE") {
return (pass);
}
# --- PURGE ---
if (req.request == "PURGE") {
# Check if the ip coresponds with the acl purge
if (!client.ip ~ purge) {
# Return error code 405 (Forbidden) when not
error 405 "Not allowed.";
}
return (lookup);
}
# --- PASSTHROUGH ---
# Always cache things with these extensions.
if (req.url ~ "\.(js|css|jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|swf)$") {
unset req.http.cookie;
return (lookup);
}
# Do not cache if HTTP authorized.
if (req.http.Authorization) {
return (pass);
}
# Skip the Varnish cache for install, update, and cron.
if (req.url ~ "install\.php|update\.php|cron\.php") {
return (pass);
}
# Skip the Varnish cache for Springboard standalone cron.
if (req.url ~ "queue_processor_cron|fundraiser_cron") {
return (pass);
}
# Pass server-status.
if (req.url ~ ".*/server-status$") {
return (pass);
}
# Check for mobile.
call mobile_detection;
# Support for Pressflow Cookie-Cache Bypass.
if (req.http.cookie ~ "NO_CACHE") {
return (pass);
}
# Force lookup if the request is a no-cache request from the client.
if (req.http.Cache-Control ~ "no-cache") {
return (pass);
}
# Don't check cache if Drupal SESSION is set.
if (req.http.cookie ~ "SESS") {
return (pass);
}
# We "hide" the non-session cookies.
if (req.http.cookie) {
set req.http.X-Varnish-Cookie = req.http.cookie;
unset req.http.cookie;
}
# --- MISC ---
# Normalize the Accept-Encoding header
# as per: http://varnish-cache.org/wiki/FAQ/Compression
if (req.http.Accept-Encoding) {
if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") {
# No point in compressing these.
unset req.http.Accept-Encoding;
}
else if (req.http.Accept-Encoding ~ "gzip") {
set req.http.Accept-Encoding = "gzip";
}
else if (req.http.Accept-Encoding ~ "deflate") {
# Next, try deflate if it is supported.
set req.http.Accept-Encoding = "deflate";
}
else {
# Unknown or deflate algorithm.
unset req.http.Accept-Encoding;
}
}
# Let's have a little grace.
set req.grace = 5m;
return (lookup);
}
sub vcl_hash {
if (req.http.cookie) {
hash_data(req.http.cookie);
}
}
# Strip any cookies before an image/js/css is inserted into cache.
sub vcl_fetch {
set beresp.grace = 5m;
# These status codes should always pass through and never cache.
if (beresp.status == 503 || beresp.status == 500) {
set beresp.http.X-Cacheable = "NO: obj.status";
set beresp.http.X-Cacheable-status = beresp.status;
return (hit_for_pass);
}
if (req.url ~ "\.(js|css|jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|swf)(\?[a-z0-9]+)?$") {
unset beresp.http.set-cookie;
}
else if (beresp.http.Cache-Control) {
unset beresp.http.Expires;
}
if (beresp.status == 301) {
set beresp.ttl = 1h;
return(deliver);
}
# All tests passed, therefore item is cacheable
set beresp.http.X-Cacheable = "YES";
}
include "devicedetect.vcl";
sub mobile_detection {
# Set the default to PC.
set req.http.X-Varnish-Device = "pc";
# Test the user agent for mobile devices.
# @see https://github.com/varnish/varnish-devicedetect
call devicedetect;
if (req.http.X-UA-Device ~ "^mobile") {
# Lump all mobile-style devices together.
set req.http.X-Varnish-Device = "mobile";
}
# Test the URL for mobile paths.
if (req.http.X-Varnish-Device == "mobile" && req.url !~ "^/((secure|donate|blog)/?.*)?$") {
# If not in the allowed paths, change back to PC.
set req.http.X-Varnish-Device = "pc";
}
# Force to PC when skipMobileDetection cookie is set.
if (req.http.cookie ~ "skipMobileDetection") {
set req.http.X-Varnish-Device = "pc";
}
# Redirect to mobile site if not already there.
if (req.http.X-Varnish-Device == "mobile" && req.http.host !~ "^m\.") {
error 750 "m.irc" + req.url;
}
# Redirect to normal site if not already there.
else if (req.http.X-Varnish-Device == "pc" && req.http.host ~ "^m\.") {
error 750 "irc" + req.url;
}
}
# Set a header to track a cache HIT/MISS.
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-Varnish-Cache = "HIT";
set resp.http.X-Varnish-Hits = obj.hits;
}
else {
set resp.http.X-Varnish-Cache = "MISS";
}
# Set a header to track the webhead.
set resp.http.X-Varnish-IP = server.ip;
}
sub vcl_hit {
if (req.request == "PURGE") {
purge;
error 200 "Purged.";
}
}
sub vcl_miss {
if (req.http.X-Varnish-Cookie) {
set bereq.http.cookie = req.http.X-Varnish-Cookie;
unset bereq.http.X-Varnish-Cookie;
}
if (req.request == "PURGE") {
purge;
error 200 "Purged.";
}
}
sub vcl_error {
# Do any redirects necessary.
if (obj.status == 750) {
set obj.http.Location = "http://" + obj.response;
set obj.status = 302;
return (deliver);
}
# Let's deliver a friendlier error page.
set obj.http.Content-Type = "text/html; charset=utf-8";
synthetic {"
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>"} + obj.status + " " + obj.response + {"</title>
</head>
<body>
<div id="page">
<h1>Page Could Not Be Loaded</h1>
<p>We're very sorry, but the page could not be loaded properly. This should be fixed very soon, and we apologize for any inconvenience.</p>
<hr />
<h4>Debug Info:</h4>
<pre>Status: "} + obj.status + {"
Response: "} + obj.response + {"
XID: "} + req.xid + {"</pre>
</div>
</body>
</html>
"};
return (deliver);
}
#
# detectdevice.vcl - regex based device detection for Varnish
# http://github.com/varnish/varnish-devicedetect/
#
# Author: Lasse Karstensen <lasse@varnish-software.com>
sub devicedetect {
unset req.http.X-UA-Device;
set req.http.X-UA-Device = "pc";
if (req.http.User-Agent ~ "(?i)(ads|google|bing|msn|yandex|baidu|ro|career|)bot" ||
req.http.User-Agent ~ "(?i)(baidu|symantec)spider" ||
req.http.User-Agent ~ "(?i)scanner" ||
req.http.User-Agent ~ "(?i)(web)crawler") {
set req.http.X-UA-Device = "bot"; }
elsif (req.http.User-Agent ~ "(?i)ip(hone|od)") { set req.http.X-UA-Device = "mobile-iphone"; }
elsif (req.http.User-Agent ~ "(?i)ipad") { set req.http.X-UA-Device = "tablet-ipad"; }
# how do we differ between an android phone and an android tablet?
# http://stackoverflow.com/questions/5341637/how-do-detect-android-tablets-in-general-useragent
elsif (req.http.User-Agent ~ "(?i)android.*(mobile|mini)") { set req.http.X-UA-Device = "mobile-android"; }
# android 3/honeycomb was just about tablet-only, and any phones will probably handle a bigger page layout.
elsif (req.http.User-Agent ~ "(?i)android 3") { set req.http.X-UA-Device = "tablet-android"; }
# may very well give false positives towards android tablets. Suggestions welcome.
elsif (req.http.User-Agent ~ "(?i)android") { set req.http.X-UA-Device = "tablet-android"; }
elsif (req.http.User-Agent ~ "^HTC" ||
req.http.User-Agent ~ "Fennec" ||
req.http.User-Agent ~ "IEMobile" ||
req.http.User-Agent ~ "BlackBerry" ||
req.http.User-Agent ~ "SymbianOS.*AppleWebKit" ||
req.http.User-Agent ~ "Opera Mobi") {
set req.http.X-UA-Device = "mobile-smartphone";
}
elsif (req.http.User-Agent ~ "(?i)symbian" ||
req.http.User-Agent ~ "(?i)^sonyericsson" ||
req.http.User-Agent ~ "(?i)^nokia" ||
req.http.User-Agent ~ "(?i)^samsung" ||
req.http.User-Agent ~ "(?i)^lg" ||
req.http.User-Agent ~ "(?i)bada" ||
req.http.User-Agent ~ "(?i)blazer" ||
req.http.User-Agent ~ "(?i)cellphone" ||
req.http.User-Agent ~ "(?i)iemobile" ||
req.http.User-Agent ~ "(?i)midp-2.0" ||
req.http.User-Agent ~ "(?i)u990" ||
req.http.User-Agent ~ "(?i)netfront" ||
req.http.User-Agent ~ "(?i)opera mini" ||
req.http.User-Agent ~ "(?i)palm" ||
req.http.User-Agent ~ "(?i)nintendo wii" ||
req.http.User-Agent ~ "(?i)playstation portable" ||
req.http.User-Agent ~ "(?i)portalmmm" ||
req.http.User-Agent ~ "(?i)proxinet" ||
req.http.User-Agent ~ "(?i)sonyericsson" ||
req.http.User-Agent ~ "(?i)symbian" ||
req.http.User-Agent ~ "(?i)windows\ ?ce" ||
req.http.User-Agent ~ "(?i)winwap" ||
req.http.User-Agent ~ "(?i)eudoraweb" ||
req.http.User-Agent ~ "(?i)htc" ||
req.http.User-Agent ~ "(?i)240x320" ||
req.http.User-Agent ~ "(?i)avantgo") {
set req.http.X-UA-Device = "mobile-generic";
}
# handle overrides
if (req.http.Cookie ~ "(i?)X-UA-Device-force") {
# ;?? means zero or one ;, non-greedy to match the first.
set req.http.X-UA-Device = regsub(req.http.Cookie, "(?i).*X-UA-Device-force=([^;]+);??.*", "\1");
}
}
# vim: sw=4:tw=120 # meh
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment