@@ -16,37 +16,55 @@ acl purge {
1616 "::1" ;
1717}
1818
19- sub vcl_recv {
19+ sub vcl_recv {
20+ # Announce support for Edge Side Includes by setting the Surrogate-Capability header
21+ set req.http.Surrogate-Capability = "Varnish=ESI/1.0" ;
22+
2023 # Remove empty query string parameters
21- # e.g.: www.example.com/index.html?
24+ # e.g.: www.example.com/index.html?
2225 if (req.url ~ "\? $" ) {
2326 set req.url = regsub (req.url , "\? $" , "" );
2427 }
2528
2629 # Remove port number from host header
2730 set req.http.Host = regsub (req.http.Host , ":[0-9]+" , "" );
28-
29- # Sorts query string parameters alphabetically for cache normalization purposes
31+
32+ # Sorts query string parameters alphabetically for cache normalization purposes.
3033 set req.url = std.querysort(req.url );
31-
34+
3235 # Remove the proxy header to mitigate the httpoxy vulnerability
33- # See https://httpoxy.org/
36+ # See https://httpoxy.org/
3437 unset req.http.proxy ;
35-
36- # Purge logic to remove objects from the cache.
37- # Tailored to the Proxy Cache Purge
38- if (req.method == "PURGE" ) {
38+
39+ # Add X-Forwarded-Proto header when using https
40+ if (! req.http.X-Forwarded-Proto && (std.port (server.ip ) == 443 )) {
41+ set req.http.X-Forwarded-Proto = "https" ;
42+ }
43+
44+ # Ban logic to remove multiple objects from the cache at once. Tailored to Drupal's cache invalidation mechanism
45+ if (req.method == "BAN" ) {
3946 if (! client.ip ~ purge) {
40- return (synth(405 ,"PURGE not allowed for this IP address" ));
47+ return (synth(405 , "BAN not allowed for this IP address" ));
48+ }
49+
50+ if (req.http.Purge-Cache-Tags ) {
51+ ban("obj.http.Purge-Cache-Tags ~ " + req.http.Purge-Cache-Tags );
4152 }
42- if ( req.http.X-Purge-Method == "regex" ) {
53+ else {
4354 ban("obj.http.x-url ~ " + req.url + " && obj.http.x-host == " + req.http.host );
44- return (synth(200 , "Purged" ));
4555 }
46- ban( "obj.http.x-url == " + req.url + " && obj.http.x-host == " + req.http.host );
47- return (synth(200 , "Purged " ));
56+
57+ return (synth(200 , "Ban added. " ));
4858 }
4959
60+ # Purge logic to remove objects from the cache
61+ if (req.method == "PURGE" ) {
62+ if (! client.ip ~ purge) {
63+ return (synth(405 ,"PURGE not allowed for this IP address" ));
64+ }
65+ return (purge);
66+ }
67+
5068 # Only handle relevant HTTP request methods
5169 if (
5270 req.method != "GET" &&
@@ -60,7 +78,7 @@ sub vcl_recv {
6078 ) {
6179 return (pipe);
6280 }
63-
81+
6482 # Remove tracking query string parameters used by analytics tools
6583 if (req.url ~ "(\? |&)(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=" ) {
6684 set req.url = regsuball (req.url , "&(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=([A-z0-9_\-\. %25]+)" , "" );
@@ -70,8 +88,7 @@ sub vcl_recv {
7088 }
7189
7290 # Only cache GET and HEAD requests
73- if (req.method != "GET" && req.method != "HEAD" ) {
74- set req.http.X-Cacheable = "NO:REQUEST-METHOD" ;
91+ if ((req.method != "GET" && req.method != "HEAD" ) || req.http.Authorization ) {
7592 return (pass );
7693 }
7794
@@ -83,158 +100,72 @@ sub vcl_recv {
83100 return (hash );
84101 }
85102
86- # No caching of special URLs, logged in users and some plugins
87- if (
88- req.http.Cookie ~ "wordpress_(?!test_)[a-zA-Z0-9_]+|wp-postpass|comment_author_[a-zA-Z0-9_]+|woocommerce_cart_hash|woocommerce_items_in_cart|wp_woocommerce_session_[a-zA-Z0-9]+|wordpress_logged_in_|comment_author|PHPSESSID" ||
89- req.http.Authorization ||
90- req.url ~ "add_to_cart" ||
91- req.url ~ "edd_action" ||
92- req.url ~ "nocache" ||
93- req.url ~ "^/addons" ||
94- req.url ~ "^/bb-admin" ||
95- req.url ~ "^/bb-login.php" ||
96- req.url ~ "^/bb-reset-password.php" ||
97- req.url ~ "^/cart" ||
98- req.url ~ "^/checkout" ||
99- req.url ~ "^/control.php" ||
100- req.url ~ "^/login" ||
101- req.url ~ "^/logout" ||
102- req.url ~ "^/lost-password" ||
103- req.url ~ "^/my-account" ||
104- req.url ~ "^/product" ||
105- req.url ~ "^/register" ||
106- req.url ~ "^/register.php" ||
107- req.url ~ "^/server-status" ||
108- req.url ~ "^/signin" ||
109- req.url ~ "^/signup" ||
110- req.url ~ "^/stats" ||
111- req.url ~ "^/wc-api" ||
112- req.url ~ "^/wp-admin" ||
113- req.url ~ "^/wp-comments-post.php" ||
114- req.url ~ "^/wp-cron.php" ||
115- req.url ~ "^/wp-login.php" ||
116- req.url ~ "^/wp-activate.php" ||
117- req.url ~ "^/wp-mail.php" ||
118- req.url ~ "^/wp-login.php" ||
119- req.url ~ "^\? add-to-cart=" ||
120- req.url ~ "^\? wc-api=" ||
121- req.url ~ "^/preview=" ||
122- req.url ~ "^/\. well-known/acme-challenge/"
123- ) {
124- set req.http.X-Cacheable = "NO:Logged in/Got Sessions" ;
125- if (req.http.X-Requested-With == "XMLHttpRequest" ) {
126- set req.http.X-Cacheable = "NO:Ajax" ;
103+ # Don't cache the following pages
104+ if (req.url ~ "^/status.php$" ||
105+ req.url ~ "^/update.php$" ||
106+ req.url ~ "^/cron.php$" ||
107+ req.url ~ "^/admin$" ||
108+ req.url ~ "^/admin/.*$" ||
109+ req.url ~ "^/flag/.*$" ||
110+ req.url ~ "^.*/ajax/.*$" ||
111+ req.url ~ "^.*/ahah/.*$" ||
112+ req.url ~ "^/\. well-known/acme-challenge/" ) {
113+ return (pass );
114+ }
115+
116+ # Remove all cookies except the session & NO_CACHE cookies
117+ if (req.http.Cookie ) {
118+ set req.http.Cookie = ";" + req.http.Cookie ;
119+ set req.http.Cookie = regsuball (req.http.Cookie , "; +" , ";" );
120+ set req.http.Cookie = regsuball (req.http.Cookie , ";(S?SESS[a-z0-9]+|NO_CACHE)=" , "; \1 =" );
121+ set req.http.Cookie = regsuball (req.http.Cookie , ";[^ ][^;]*" , "" );
122+ set req.http.Cookie = regsuball (req.http.Cookie , "^[; ]+|[; ]+$" , "" );
123+
124+ if (req.http.cookie ~ "^\s *$" ) {
125+ unset req.http.cookie ;
126+ } else {
127+ return (pass );
127128 }
128- return (pass );
129129 }
130-
131- # Remove x-cache-status header
132- unset req.http.x-cache-status ;
133-
134- # Remove any cookies left
135- unset req.http.Cookie ;
136130 return (hash );
137131}
138132
139133sub vcl_hash {
140- if (req.http.X-Forwarded-Proto ) {
141- # Create cache variations depending on the request protocol
142- hash_data(req.http.X-Forwarded-Proto );
143- }
144- }
145-
146- sub vcl_hit {
147- set req.http.x-cache-status = "HIT" ;
148- if (obj.ttl <= 0s && obj.grace > 0s ) {
149- set req.http.x-cache-status = "HIT graced" ;
150- }
151-
152- if (req.method == "PURGE" ) {
153- return (synth(200 , "OK" ));
154- }
155- }
156-
157- sub vcl_miss {
158- set req.http.x-cache-status = "MISS" ;
159-
160- if (req.method == "PURGE" ) {
161- return (synth(404 , "Not cached" ));
162- }
163- }
164-
165- sub vcl_pass {
166- set req.http.x-cache-status = "PASS" ;
167- }
168-
169- sub vcl_pipe {
170- set req.http.x-cache-status = "pipe uncacheable" ;
171- }
172-
173- sub vcl_synth {
174- set req.http.x-cache-status = "synth synth" ;
175- # uncomment the following line to show the information in the response
176- set resp.http.x-cache-status = req.http.x-cache-status ;
177-
178- # redirect for http
179- if (resp.status == 750 ) {
180- set resp.status = 301 ;
181- set resp.http.Location = req.http.x-redir ;
182- return (deliver );
183- }
134+ # Create cache variations depending on the request protocol
135+ hash_data(req.http.X-Forwarded-Proto );
184136}
185137
186138sub vcl_backend_response {
187139 # Inject URL & Host header into the object for asynchronous banning purposes
188140 set beresp.http.x-url = bereq.url ;
189141 set beresp.http.x-host = bereq.http.host ;
190142
191- # If we dont get a Cache-Control header from the backend
192- # we default to 1h cache for all objects
193- if (! beresp.http.Cache-Control ) {
194- set beresp.ttl = 1h ;
195- set beresp.http.X-Cacheable = "YES:Forced" ;
196- }
143+ # Serve stale content for 2 minutes after object expiration
144+ # Perform asynchronous revalidation while stale content is served
145+ set beresp.grace = 120s ;
197146
198147 # If the file is marked as static we cache it for 1 day
199148 if (bereq.http.X-Static-File == "true" ) {
200149 unset beresp.http.Set-Cookie ;
201- set beresp.http.X-Cacheable = "YES:Forced" ;
202150 set beresp.ttl = 1d ;
203151 }
204152
205- # Remove the Set-Cookie header when a specific Wordfence cookie is set
206- if (beresp.http.Set-Cookie ~ "wfvt_|wordfence_verifiedHuman" ) {
207- unset beresp.http.Set-Cookie ;
153+ # If we dont get a Cache-Control header from the backend
154+ # we default to 1h cache for all objects
155+ if (! beresp.http.Cache-Control ) {
156+ set beresp.ttl = 1h ;
208157 }
209-
210- if (beresp.http.Set-Cookie ) {
211- set beresp.http.X-Cacheable = "NO:Got Cookies" ;
212- } elseif (beresp.http.Cache-Control ~ "private" ) {
213- set beresp.http.X-Cacheable = "NO:Cache-Control=private" ;
214- }
215- }
216158
217- sub vcl_deliver {
218- # oh noes backend is down
219- if (resp.status == 503 ) {
220- return (restart );
221- }
222-
223- # Debug header
224- if (req.http.X-Cacheable ) {
225- set resp.http.X-Cacheable = req.http.X-Cacheable ;
226- } elseif (obj.uncacheable) {
227- if (! resp.http.X-Cacheable ) {
228- set resp.http.X-Cacheable = "NO:UNCACHEABLE" ;
229- }
230- } elseif (! resp.http.X-Cacheable ) {
231- set resp.http.X-Cacheable = "YES" ;
159+ # Parse Edge Side Include tags when the Surrogate-Control header contains ESI/1.0
160+ if (beresp.http.Surrogate-Control ~ "ESI/1.0" ) {
161+ unset beresp.http.Surrogate-Control ;
162+ set beresp.do_esi = true ;
232163 }
164+ }
233165
234- set resp.http.x-cache-status = req.http.x-cache-status ;
235- set resp.http.x-varnish = resp.http.x-varnish + " " + req.http.x-cache-status ;
236-
166+ sub vcl_deliver {
237167 # Cleanup of headers
238168 unset resp.http.x-url ;
239- unset resp.http.x-host ;
169+ unset resp.http.x-host ;
170+ unset req.http.X-Static-File ;
240171}
0 commit comments