├── default.vcl └── README.md /default.vcl: -------------------------------------------------------------------------------- 1 | // Imports 2 | import std; // import logging 3 | 4 | // Defining our backends 5 | backend myFirstServer { 6 | .host = "myFirstServer.mclear.co.uk"; 7 | .port = "8080"; 8 | .probe = { 9 | .url = "/index.html"; 10 | .interval = 5s; 11 | .timeout = 1 s; 12 | .window = 5; 13 | .threshold = 3; 14 | } 15 | .connect_timeout = 600s; 16 | .first_byte_timeout = 600s; 17 | .between_bytes_timeout = 600s; 18 | } 19 | 20 | backend mySecondServer { 21 | .host = "mySecondServer.mclear.co.uk"; 22 | .port = "8080"; 23 | .probe = { 24 | .url = "/index.html"; 25 | .interval = 5s; 26 | .timeout = 1 s; 27 | .window = 5; 28 | .threshold = 3; 29 | } 30 | .connect_timeout = 600s; 31 | .first_byte_timeout = 600s; 32 | .between_bytes_timeout = 600s; 33 | } 34 | 35 | // Defining our cluster including end points for purge 36 | director cluster round-robin { 37 | {.backend = myFirstServer;} 38 | {.backend = mySecondServer;} 39 | } 40 | 41 | // End points for purge requests 42 | acl purge { 43 | "myFirstServer.mclear.co.uk"; 44 | "mySecondServer.mclear.co.uk"; 45 | } 46 | 47 | sub vcl_fetch{ 48 | // When fetching images we can set a long caching marker that we can access later 49 | if (req.request == "GET" && req.url ~ "\.(jpg|jpeg|gif|ico|css|js|png)$") { 50 | set beresp.http.magicmarker = "1"; 51 | } 52 | // Don't cache mobile requests 53 | if (req.http.X-Device == "mobile"){ 54 | set beresp.ttl = 0s; 55 | std.log("Not caching mobile requests"); 56 | } 57 | // Don't cache error pages 58 | if (beresp.status == 404 || beresp.status == 503 || beresp.status >= 500){ 59 | set beresp.ttl = 0s; 60 | } 61 | 62 | // Some debug code for why objects are/aren't cachable 63 | // Varnish determined the object was not cacheable 64 | if (!beresp.ttl > 0s) { 65 | set beresp.http.X-Cacheable = "NO:Not Cacheable"; 66 | 67 | // You don't wish to cache content for logged in users 68 | } elsif (req.http.Cookie ~ "(UserID|_session)") { 69 | set beresp.http.X-Cacheable = "NO:Got Session"; 70 | std.log("It appears a session is in process so we have returned pass"); 71 | return(deliver); 72 | 73 | // You are respecting the Cache-Control=private header from the backend 74 | } elsif (beresp.http.Cache-Control ~ "private") { 75 | set beresp.http.X-Cacheable = "NO:Cache-Control=private"; 76 | std.log("It appears this is private so we have returned pass"); 77 | return(deliver); 78 | 79 | // You are extending the lifetime of the object artificially 80 | } elsif (beresp.ttl < 1s) { 81 | set beresp.ttl = 5s; 82 | set beresp.grace = 5s; 83 | set beresp.http.X-Cacheable = "YES:FORCED"; 84 | // Varnish determined the object was cacheable 85 | } else { 86 | set beresp.http.X-Cacheable = "YES"; 87 | } 88 | } 89 | 90 | sub vcl_recv 91 | { 92 | set req.http.X-Forwarded-For = client.ip; // Set the client IP 93 | set req.backend = cluster; // Set the backend to the cluster 94 | call device_detection; // Check for a mobile device 95 | 96 | // Purge WordPress requests for purge 97 | if (req.request == "PURGE") { 98 | if (!client.ip ~ purge) { 99 | error 405 "Not allowed."; 100 | } 101 | ban("req.url = " + req.url + " && req.http.host = " + req.http.host); 102 | error 200 "Purged."; 103 | } 104 | 105 | // Cache static objects such as images 106 | if (req.request == "GET" && req.url ~ "\.(jpg|jpeg|gif|ico|css|js|png)$") { 107 | unset req.http.cookie; 108 | std.log("request is for a file such as jpg jpeg etc so dropping cookie"); 109 | return(lookup); 110 | } 111 | 112 | /* 113 | // Cache any dynamic content 114 | if (req.url !~ "wp-(login|admin|signup)" && req.url !~ "preview" || req.url ~ "admin-ajax.php"){ 115 | std.log("Request is not for login, admin, preview, sign up or admin-ajax so don't cache it"); 116 | if (req.http.Cookie !~ "wordpress_logged_in "){ 117 | std.log("User is not logged in"); 118 | if (req.http.Cookie !~ "wp-postpass"){ 119 | std.log("Post is not password protected"); 120 | unset req.http.cookie; 121 | return(lookup); 122 | } 123 | } 124 | } 125 | } 126 | */ 127 | } 128 | 129 | sub vcl_deliver { 130 | if (resp.http.magicmarker) { 131 | std.log("Magicmarker set so setting our own client side caching"); 132 | unset resp.http.magicmarker; 133 | set resp.http.Cache-Control = "max-age=648000"; 134 | set resp.http.Expires = "Thu, 01 May 2014 00:10:22 GMT"; 135 | set resp.http.Last-Modified = "Mon, 25 Apr 2011 01:00:00 GMT"; 136 | set resp.http.Age = "647"; 137 | } 138 | } 139 | 140 | sub device_detection { 141 | // Default to thinking it's a PC 142 | set req.http.X-Device = "pc"; 143 | 144 | // Add all possible agent strings - These are the most popular agent strings 145 | std.log("Checking for mobile device"); 146 | if (req.http.User-Agent ~ "iP(hone|od)" || req.http.User-Agent ~ "Android" || req.http.User-Agent ~ "Symbian" || req.http.User-Agent ~ "^BlackBerry" || req.http.User-Agent ~ "^SonyEricsson" 147 | || req.http.User-Agent ~ "^Nokia" || req.http.User-Agent ~ "^SAMSUNG" || req.http.User-Agent ~ "^LG" || req.http.User-Agent ~ "webOS") { 148 | std.log("Mobile device detected"); 149 | std.log("Following req.url"); 150 | if (req.url !~ "wptouch_view=normal"){ 151 | std.log("wptouch_switch_toggle is not set"); 152 | set req.http.X-Device = "mobile"; 153 | } 154 | else{ 155 | std.log("this should not be redirecting to mobile"); 156 | std.log("wp touch view is set to normal so we shouldn't be setting a device type other thna PC"); 157 | set req.http.X-Device = "pc"; 158 | error 750 req.http.host; 159 | } 160 | } 161 | 162 | // These are some more obscure agent strings 163 | if (req.http.User-Agent ~ "^PalmSource"){ 164 | set req.http.X-Device = "mobile"; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Note this repo is outdated, I don't actively maintain it as I don't maintain Varnish servers any more. Feel free to fork/fix/pull - If this repo has no activity by the end of 2015 I will remove it. 2 | 3 | #Varnish Wordpress VCLs 4 | 5 | [Details here](http://mclear.co.uk/2011/10/05/wordpress-varnish-cache-config-vcl/) 6 | 7 | ## Varnish 4 VCL 8 | ``` 9 | # 10 | # This is an example VCL file for Varnish. 11 | # 12 | # It does not do anything by default, delegating control to the 13 | # builtin VCL. The builtin VCL is called when there is no explicit 14 | # return statement. 15 | # 16 | # See the VCL chapters in the Users Guide at https://www.varnish-cache.org/docs/ 17 | # and http://varnish-cache.org/trac/wiki/VCLExamples for more examples. 18 | 19 | # Update of varnish 4 to work with wordpress 20 | # Marker to tell the VCL compiler that this VCL has been adapted to the 21 | # new 4.0 format. 22 | vcl 4.0; 23 | 24 | # Default backend definition. Set this to point to your content server. 25 | backend default { 26 | .host = "127.0.0.1"; 27 | .port = "8080"; 28 | .connect_timeout = 600s; 29 | .first_byte_timeout = 600s; 30 | .between_bytes_timeout = 600s; 31 | .max_connections = 800; 32 | } 33 | 34 | # Only allow purging from specific IPs 35 | acl purge { 36 | "localhost"; 37 | "127.0.0.1"; 38 | } 39 | 40 | # This function is used when a request is send by a HTTP client (Browser) 41 | sub vcl_recv { 42 | # Normalize the header, remove the port (in case you're testing this on various TCP ports) 43 | set req.http.Host = regsub(req.http.Host, ":[0-9]+", ""); 44 | 45 | # Remove has_js and CloudFlare/Google Analytics __* cookies. 46 | set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(_[_a-z]+|has_js)=[^;]*", ""); 47 | # Remove a ";" prefix, if present. 48 | set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", ""); 49 | 50 | # Allow purging from ACL 51 | if (req.method == "PURGE") { 52 | # If not allowed then a error 405 is returned 53 | if (!client.ip ~ purge) { 54 | return(synth(405, "This IP is not allowed to send PURGE requests.")); 55 | } 56 | # If allowed, do a cache_lookup -> vlc_hit() or vlc_miss() 57 | return (purge); 58 | } 59 | 60 | # Post requests will not be cached 61 | if (req.http.Authorization || req.method == "POST") { 62 | return (pass); 63 | } 64 | 65 | # --- WordPress specific configuration 66 | 67 | # Did not cache the admin and login pages 68 | if (req.url ~ "wp-(login|admin)" || req.url ~ "preview=true") { 69 | return (pass); 70 | } 71 | 72 | # Remove the "has_js" cookie 73 | set req.http.Cookie = regsuball(req.http.Cookie, "has_js=[^;]+(; )?", ""); 74 | 75 | # Remove any Google Analytics based cookies 76 | set req.http.Cookie = regsuball(req.http.Cookie, "__utm.=[^;]+(; )?", ""); 77 | 78 | # Remove the Quant Capital cookies (added by some plugin, all __qca) 79 | set req.http.Cookie = regsuball(req.http.Cookie, "__qc.=[^;]+(; )?", ""); 80 | 81 | # Remove the wp-settings-1 cookie 82 | set req.http.Cookie = regsuball(req.http.Cookie, "wp-settings-1=[^;]+(; )?", ""); 83 | 84 | # Remove the wp-settings-time-1 cookie 85 | set req.http.Cookie = regsuball(req.http.Cookie, "wp-settings-time-1=[^;]+(; )?", ""); 86 | 87 | # Remove the wp test cookie 88 | set req.http.Cookie = regsuball(req.http.Cookie, "wordpress_test_cookie=[^;]+(; )?", ""); 89 | 90 | # Are there cookies left with only spaces or that are empty? 91 | if (req.http.cookie ~ "^ *$") { 92 | unset req.http.cookie; 93 | } 94 | 95 | # Cache the following files extensions 96 | if (req.url ~ "\.(css|js|png|gif|jp(e)?g|swf|ico)") { 97 | unset req.http.cookie; 98 | } 99 | 100 | # Normalize Accept-Encoding header and compression 101 | # https://www.varnish-cache.org/docs/3.0/tutorial/vary.html 102 | if (req.http.Accept-Encoding) { 103 | # Do no compress compressed files... 104 | if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") { 105 | unset req.http.Accept-Encoding; 106 | } elsif (req.http.Accept-Encoding ~ "gzip") { 107 | set req.http.Accept-Encoding = "gzip"; 108 | } elsif (req.http.Accept-Encoding ~ "deflate") { 109 | set req.http.Accept-Encoding = "deflate"; 110 | } else { 111 | unset req.http.Accept-Encoding; 112 | } 113 | } 114 | 115 | # Check the cookies for wordpress-specific items 116 | if (req.http.Cookie ~ "wordpress_" || req.http.Cookie ~ "comment_") { 117 | return (pass); 118 | } 119 | if (!req.http.cookie) { 120 | unset req.http.cookie; 121 | } 122 | 123 | # --- End of WordPress specific configuration 124 | 125 | # Did not cache HTTP authentication and HTTP Cookie 126 | if (req.http.Authorization || req.http.Cookie) { 127 | # Not cacheable by default 128 | return (pass); 129 | } 130 | 131 | # Cache all others requests 132 | return (hash); 133 | } 134 | 135 | sub vcl_pipe { 136 | return (pipe); 137 | } 138 | 139 | sub vcl_pass { 140 | return (fetch); 141 | } 142 | 143 | # The data on which the hashing will take place 144 | sub vcl_hash { 145 | hash_data(req.url); 146 | if (req.http.host) { 147 | hash_data(req.http.host); 148 | } else { 149 | hash_data(server.ip); 150 | } 151 | 152 | # If the client supports compression, keep that in a different cache 153 | if (req.http.Accept-Encoding) { 154 | hash_data(req.http.Accept-Encoding); 155 | } 156 | 157 | return (lookup); 158 | } 159 | 160 | # This function is used when a request is sent by our backend (Nginx server) 161 | sub vcl_backend_response { 162 | # Remove some headers we never want to see 163 | unset beresp.http.Server; 164 | unset beresp.http.X-Powered-By; 165 | 166 | # For static content strip all backend cookies 167 | if (bereq.url ~ "\.(css|js|png|gif|jp(e?)g)|swf|ico") { 168 | unset beresp.http.cookie; 169 | } 170 | # Don't store backend 171 | if (bereq.url ~ "wp-(login|admin)" || bereq.url ~ "preview=true") { 172 | set beresp.uncacheable = true; 173 | set beresp.ttl = 30s; 174 | return (deliver); 175 | } 176 | 177 | # Only allow cookies to be set if we're in admin area 178 | if (!(bereq.url ~ "(wp-login|wp-admin|preview=true)")) { 179 | unset beresp.http.set-cookie; 180 | } 181 | 182 | # don't cache response to posted requests or those with basic auth 183 | if ( bereq.method == "POST" || bereq.http.Authorization ) { 184 | set beresp.uncacheable = true; 185 | set beresp.ttl = 120s; 186 | return (deliver); 187 | } 188 | 189 | # don't cache search results 190 | if ( bereq.url ~ "\?s=" ){ 191 | set beresp.uncacheable = true; 192 | set beresp.ttl = 120s; 193 | return (deliver); 194 | } 195 | 196 | # only cache status ok 197 | if ( beresp.status != 200 ) { 198 | set beresp.uncacheable = true; 199 | set beresp.ttl = 120s; 200 | return (deliver); 201 | } 202 | 203 | # A TTL of 2h 204 | set beresp.ttl = 2h; 205 | # Define the default grace period to serve cached content 206 | set beresp.grace = 30s; 207 | 208 | return (deliver); 209 | } 210 | 211 | # The routine when we deliver the HTTP request to the user 212 | # Last chance to modify headers that are sent to the client 213 | sub vcl_deliver { 214 | if (obj.hits > 0) { 215 | set resp.http.X-Cache = "cached"; 216 | } else { 217 | set resp.http.x-Cache = "uncached"; 218 | } 219 | 220 | # Remove some headers: PHP version 221 | unset resp.http.X-Powered-By; 222 | 223 | # Remove some headers: Apache version & OS 224 | unset resp.http.Server; 225 | 226 | # Remove some heanders: Varnish 227 | unset resp.http.Via; 228 | unset resp.http.X-Varnish; 229 | 230 | return (deliver); 231 | } 232 | 233 | sub vcl_init { 234 | return (ok); 235 | } 236 | 237 | sub vcl_fini { 238 | return (ok); 239 | } 240 | ``` 241 | --------------------------------------------------------------------------------