# # Customized VCL file for serving up a Drupal site with multiple back-ends. # # For more information on this VCL, visit the Lullabot article: # http://www.lullabot.com/articles/varnish-multiple-web-servers-drupal # # Define the internal network subnet. # These are used below to allow internal access to certain files while not # allowing access from the public internet. acl internal { "127.0.0.1"; "localhost"; } backend default { .host = "127.0.0.1"; .port = "8080"; .connect_timeout = 600s; .first_byte_timeout = 600s; .between_bytes_timeout = 600s; # enable to use status script for health of backend # .probe = { .url = "/status.php"; .interval = 5s; .timeout = 1s; .window = 5;.threshold = 3; } } # Respond to incoming requests. sub vcl_recv { # reject malicious requests if (client.ip ~ blacklisted) { error 503 "Your IP has been blocked."; } # Allow the backend to serve up stale content if it is responding slowly. set req.grace = 6h; # Do not cache these paths. if (req.url ~ "^/status\.php$" || req.url ~ "^/update\.php$" || req.url ~ "^/ooyala/ping$" || req.url ~ "^/admin/build/features" || req.url ~ "^/info/.*$" || req.url ~ "^/flag/.*$" || req.url ~ "^.*/ajax/.*$" || req.url ~ "^/phpMyAdmin/.*$" || req.url ~ "^.*/ahah/.*$") { return (pass); } # Pipe these paths directly to Apache for streaming. if (req.url ~ "^/admin/content/backup_migrate/export") { return (pipe); } # Do not allow outside access to cron.php or install.php. if (req.url ~ "^/(cron|install)\.php$" && !client.ip ~ internal) { # Have Varnish throw the error directly. error 404 "Page not found."; # Use a custom error page that you've defined in Drupal at the path "404". # set req.url = "/404"; } # Handle compression correctly. Different browsers send different # "Accept-Encoding" headers, even though they mostly all support the same # compression mechanisms. By consolidating these compression headers into # a consistent format, we can reduce the size of the cache and get more hits.= # @see: http:// varnish.projects.linpro.no/wiki/FAQ/Compression if (req.http.Accept-Encoding) { if (req.http.Accept-Encoding ~ "gzip") { # If the browser supports it, we'll use 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 algorithm. Remove it and send unencoded. unset req.http.Accept-Encoding; } } if ((!(req.url ~ "(^/(admin|users|cache|misc|modules|sites|system|openid|themes|node/add|node/*/edit|user/*|workspace|imce))|(/(comment/reply|edit|logout|user|user/(login|password|register))$)")) && (!(req.http.Cookie ~ "(VARNISH|DRUPAL_UID|LOGGED_IN|SESS)"))) { unset req.http.Cookie; } # Always cache the following file types for all users. if (req.url ~ "(?i)\.(png|gif|jpeg|jpg|ico|swf|css|js|html|htm|pdf|doc)(\?[a-z0-9]+)?$") { unset req.http.Cookie; } # Remove all cookies that Drupal doesn't need to know about. ANY remaining # cookie will cause the request to pass-through to Apache. For the most part # we always set the NO_CACHE cookie after any POST request, disabling the # Varnish cache temporarily. The session cookie allows all authenticated users # to pass through as long as they're logged in. if (req.http.Cookie) { # Specifically remove Google analytics and Piwik cookies set req.http.Cookie = regsuball(req.http.Cookie, "(^|; ) *__utm.=[^;]+;? *", "\1"); # removes all cookies named __utm? (utma, utmb...) set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(_pk_(ses|id)[\.a-z0-9]*)=[^;]*", ""); # removes Piwik cookies # Don't cache Drupal logged-in user sessions # LOGGED_IN is the cookie that earlier version of Pressflow sets # VARNISH is the cookie which the varnish.module sets if (req.http.Cookie ~ "(VARNISH|DRUPAL_UID|LOGGED_IN)") { return (pass); } elseif (!(req.http.Cookie ~ "(VARNISH|DRUPAL_UID|LOGGED_IN)")) { set req.http.Cookie = ";" req.http.Cookie; set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";"); set req.http.Cookie = regsuball(req.http.Cookie, ";(SESS[a-z0-9]+|NO_CACHE)=", "; \1="); set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", ""); set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", ""); if (req.http.Cookie == "") { # If there are no remaining cookies, remove the cookie header. If there # aren't any cookie headers, Varnish's default behavior will be to cache # the page. unset req.http.Cookie; } else { # If there is any cookies left (a session or NO_CACHE cookie), do not # cache the page. Pass it on to Apache directly. return (pass); } } } } # Routine used to determine the cache key if storing/retrieving a cached page. sub vcl_hash { # Include cookie in cache hash. # This check is unnecessary because we already pass on all cookies. # if (req.http.Cookie) { # set req.hash += req.http.Cookie; # } } # Code determining what to do when serving items from the Apache servers. sub vcl_fetch { # Don't allow static files to set cookies. if (req.url ~ "(?i)\.(png|gif|jpeg|jpg|ico|swf|css|js|html|htm|pdf|doc)(\?[a-z0-9]+)?$") { # beresp == Back-end response from the web server. unset beresp.http.set-cookie; } # Allow items to be stale if needed. set beresp.grace = 6h; # Serve the good page from the cache and re-check in one minute if (beresp.status == 503 || beresp.status == 501 || beresp.status == 500) { set beresp.grace = 60s; restart; } # Anything that is cacheable, but has expiration date too low which prevents caching gets cached by varnish to take # the load off apache. if (beresp.cacheable && beresp.ttl < 1h) { ##### MINIMUM CACHE LIFETIME: Change this to your needs # Set how long Varnish will keep it set beresp.ttl = 30m; ##### END MINIMUM CACHE LIFETIME: Change this to your needs # marker for vcl_deliver to reset Age: set beresp.http.magicmarker = "1"; } } sub vcl_deliver { ### START DEBUG if (obj.hits > 0) { set resp.http.X-Varnish-Cache = "HIT Varnish (" obj.hits ")"; } else { set resp.http.X-Varnish-Cache = "MISS"; } set resp.http.X-Varnish-Debug-Hits = obj.hits; set resp.http.X-Varnish-Debug-Age = resp.http.age; ### END DEBUG # The magic marker is used to reset the age to 0 as else the object is older than its ttl. if (resp.http.magicmarker) { # Remove the magic marker unset resp.http.magicmarker; # By definition we have a fresh object set resp.http.Age = "0"; } } # In the event of an error, show friendlier messages. sub vcl_error { # Redirect to some other URL in the case of a homepage failure. #if (req.url ~ "^/?$") { # set obj.status = 302; # set obj.http.Location = "http://backup.example.com/"; #} # Otherwise redirect to the homepage, which will likely be in the cache. set obj.http.Content-Type = "text/html; charset=utf-8"; synthetic {" # Error page html usually here "}; return (deliver); }