-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathvarnish6.vcl
More file actions
341 lines (292 loc) · 11 KB
/
Copy pathvarnish6.vcl
File metadata and controls
341 lines (292 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# Optimized VCL for Magento 2 with Xkey support
vcl 4.1;
import cookie;
import std;
{{if use_xkey_vmod}}
import xkey;
{{/if}}
# The minimal Varnish version is 6.0
# For SSL offloading, pass the following header in your proxy server or load balancer: '{{var ssl_offloaded_header }}: https'
backend default {
.host = "{{var host}}";
.port = "{{var port}}";
.first_byte_timeout = 600s;
.probe = {
.url = "/health_check.php";
.timeout = 2s;
.interval = 5s;
.window = 10;
.threshold = 5;
}
}
# Access control list for purge requests
acl purge {
{{for item in access_list}}
"{{var item.ip}}";
{{/for}}
}
sub vcl_recv {
# Prevent header injection attacks
unset req.http.X-Original-URL;
unset req.http.X-Rewrite-URL;
unset req.http.X-Forwarded-Host;
unset req.http.X-Forwarded-Server;
# Mitigate HTTPoxy vulnerability (CVE-2016-5387)
unset req.http.proxy;
# Remove empty query string parameters
# e.g.: www.example.com/index.html?
if (req.url ~ "\?$") {
set req.url = regsub(req.url, "\?$", "");
}
# Remove port number from host header if set
if (req.http.Host ~ ":[0-9]+$") {
set req.http.Host = regsub(req.http.Host, ":[0-9]+$", "");
}
# Sorts query string parameters alphabetically for cache normalization purposes,
# only when there are multiple parameters
if (req.url ~ "\?.+&.+") {
set req.url = std.querysort(req.url);
}
# Reduce grace to the configured setting if the backend is healthy
# In case of an unhealthy backend, the original grace is used
if (std.healthy(req.backend_hint)) {
set req.grace = {{var grace_period}}s;
}
# Allow cache purge via Ctrl-Shift-R or Cmd-Shift-R for IP's in purge ACL list
if (req.http.pragma ~ "no-cache" || req.http.Cache-Control ~ "no-cache") {
if (client.ip ~ purge) {
set req.hash_always_miss = true;
}
}
# Purge logic to remove objects from the cache
# Tailored to Magento's cache invalidation mechanism
# The X-Magento-Tags-Pattern value is matched to the tags in the X-Magento-Tags header
# If X-Magento-Tags-Pattern is not set, a URL-based purge is executed
if (req.method == "PURGE") {
if (client.ip !~ purge) {
return (synth(405));
}
# If the X-Magento-Tags-Pattern header is not set, just use regular URL-based purge
if (!req.http.X-Magento-Tags-Pattern) {
return (purge);
}
{{if use_xkey_vmod}}
# Full Page Cache flush
if (req.http.X-Magento-Tags-Pattern == ".*") {
# If Magento wants to flush everything with .* regexp, it's faster to remove them
# using the 'all' tag. This tag is automatically added by this VCL when a backend
# is generated (see vcl_backend_response).
if (req.http.X-Magento-Purge-Soft) {
set req.http.n-gone = xkey.softpurge("all");
} else {
set req.http.n-gone = xkey.purge("all");
}
return (synth(200, req.http.n-gone));
} elseif (req.http.X-Magento-Tags-Pattern) {
# replace "((^|,)cat_c(,|$))|((^|,)cat_p(,|$))" to be "cat_c,cat_p"
set req.http.X-Magento-Tags-Pattern = regsuball(req.http.X-Magento-Tags-Pattern, "[^a-zA-Z0-9_-]+" ,",");
set req.http.X-Magento-Tags-Pattern = regsuball(req.http.X-Magento-Tags-Pattern, "(^,*)|(,*$)" ,"");
if (req.http.X-Magento-Purge-Soft) {
set req.http.n-gone = xkey.softpurge(req.http.X-Magento-Tags-Pattern);
} else {
set req.http.n-gone = xkey.purge(req.http.X-Magento-Tags-Pattern);
}
return (synth(200, req.http.n-gone));
}
{{else}}
ban("obj.http.X-Magento-Tags ~ " + req.http.X-Magento-Tags-Pattern);
return (synth(200, "0"));
{{/if}}
}
if (req.method != "GET" &&
req.method != "HEAD" &&
req.method != "PUT" &&
req.method != "POST" &&
req.method != "PATCH" &&
req.method != "TRACE" &&
req.method != "OPTIONS" &&
req.method != "DELETE") {
return (pipe);
}
# We only deal with GET and HEAD by default
if (req.method != "GET" && req.method != "HEAD") {
return (pass);
}
# Bypass health check requests
if (req.url == "/health_check.php") {
return (pass);
}
# Media files caching
if (req.url ~ "^/(pub/)?media/") {
{{if enable_media_cache}}
unset req.http.Https;
unset req.http.{{var ssl_offloaded_header}};
unset req.http.Cookie;
{{else}}
return (pass);
{{/if}}
}
# Static files caching
if (req.url ~ "^/(pub/)?static/") {
{{if enable_static_cache}}
unset req.http.Https;
unset req.http.{{var ssl_offloaded_header}};
unset req.http.Cookie;
{{else}}
return (pass);
{{/if}}
}
# Collapse multiple cookie headers into one.
# We do this, because clients often send a Cookie header for each cookie they have.
# We want to join them all together with the ';' separator, so we can parse them in one batch.
std.collect(req.http.Cookie, ";");
# Parse the cookie header.
# This means that we can use the cookie functions to check for cookie existence,
# values, etc down the line.
cookie.parse(req.http.Cookie);
{{for item in pass_on_cookie_presence}}
if (req.http.Cookie ~ "{{var item.regex}}") {
return (pass);
}
{{/for}}
# Remove all marketing/tracking get parameters to minimize the cache objects
if (req.url ~ "(\?|&)({{var tracking_parameters}})=") {
set req.url = regsuball(req.url, "({{var tracking_parameters}})=[-_A-z0-9+(){}%.]+&?", "");
set req.url = regsub(req.url, "[?|&]+$", "");
}
# Bypass authenticated GraphQL requests without a X-Magento-Cache-Id
if (req.url ~ "/graphql" && !req.http.X-Magento-Cache-Id && req.http.Authorization ~ "^Bearer") {
return (pass);
}
return (hash);
}
sub vcl_hash {
# For non-graphql requests we add the value of the Magento Vary cookie to the
# object hash. This vary cookie can contain things like currency, store code, etc.
# These variations are typically rendered server-side, so we need to cache them separately.
if (req.url !~ "/graphql" && cookie.isset("X-Magento-Vary")) {
hash_data(cookie.get("X-Magento-Vary"));
}
# To make sure http users don't see ssl warning
hash_data(req.http.{{var ssl_offloaded_header}});
{{var design_exceptions_code}}
# For graphql requests we execute the process_graphql_headers subroutine
if (req.url ~ "/graphql") {
call process_graphql_headers;
}
# Don't strip trailing slash for the homepage
if(req.url == "/") {
hash_data(req.url);
} else {
# Strip trailing slash from URL for cache normalization
hash_data(regsub(req.url, "\/$", ""));
}
# Always include the host header in the hash to separate different websites
hash_data(req.http.Host);
return(lookup);
}
sub process_graphql_headers {
# The X-Magento-Cache-Id header is used by graphql clients to let the backend
# know which variant it is. It's basically the same as the Vary # cookie, but
# for graphql requests.
if (req.http.X-Magento-Cache-Id) {
hash_data(req.http.X-Magento-Cache-Id);
# When the frontend stops sending the auth token, make sure users stop getting results cached for logged-in users
if (req.http.Authorization ~ "^Bearer") {
hash_data("Authorized");
}
}
# If store header is specified by client, add it to the hash
if (req.http.Store) {
hash_data(req.http.Store);
}
# If content-currency header is specified, add it to the hash
if (req.http.Content-Currency) {
hash_data(req.http.Content-Currency);
}
}
sub vcl_backend_response {
# Serve stale content for one day after object expiration while a fresh
# version is fetched in the background.
set beresp.grace = 1d;
{{if use_xkey_vmod}}
if (beresp.http.X-Magento-Tags) {
# set comma separated xkey with "all" tag, allowing for fast full purges
set beresp.http.XKey = beresp.http.X-Magento-Tags + ",all";
unset beresp.http.X-Magento-Tags;
}
{{/if}}
# All text-based content can be parsed as ESI
if (beresp.http.content-type ~ "text") {
set beresp.do_esi = true;
}
# Cache HTTP 200 responses
{{if enable_404_cache}}
if (beresp.status != 200 && beresp.status != 404) {
{{else}}
if (beresp.status != 200) {
{{/if}}
set beresp.ttl = 120s;
set beresp.uncacheable = true;
return (deliver);
}
# Don't cache if the request cache ID doesn't match the response cache ID for graphql requests
if (bereq.url ~ "/graphql" && bereq.http.X-Magento-Cache-Id && bereq.http.X-Magento-Cache-Id != beresp.http.X-Magento-Cache-Id) {
set beresp.ttl = 120s;
set beresp.uncacheable = true;
return (deliver);
}
# Remove the Set-Cookie header for cacheable content
# Only for HTTP GET & HTTP HEAD requests
# We remove the Set-Cookie header from the VCL response, because we want to keep
# the objects in the cache anonymous.
if (beresp.ttl > 0s && (bereq.method == "GET" || bereq.method == "HEAD")) {
unset beresp.http.Set-Cookie;
}
}
sub vcl_deliver {
if (obj.uncacheable) {
set resp.http.X-Magento-Cache-Debug = "UNCACHEABLE";
} else if (obj.hits > 0 && obj.ttl > 0s) {
set resp.http.X-Magento-Cache-Debug = "HIT";
} else if (obj.hits > 0 && obj.ttl <= 0s) {
set resp.http.X-Magento-Cache-Debug = "HIT-GRACE";
} else if(req.hash_always_miss) {
set resp.http.X-Magento-Cache-Debug = "MISS-FORCED";
} else {
set resp.http.X-Magento-Cache-Debug = "MISS";
}
# Let browser and Cloudflare cache non-static content that are cacheable for short period of time
if (resp.http.Cache-Control !~ "private" && req.url !~ "^/(media|static)/" && obj.ttl > 0s && !obj.uncacheable) {
{{if enable_bfcache}}
set resp.http.Cache-Control = "must-revalidate, max-age=60";
{{else}}
set resp.http.Cache-Control = "no-store, must-revalidate, max-age=60";
{{/if}}
}
# Remove all headers that don't provide any value for the client
{{if use_xkey_vmod}}
unset resp.http.XKey;
{{/if}}
unset resp.http.Expires;
unset resp.http.Pragma;
unset resp.http.X-Magento-Debug;
unset resp.http.X-Magento-Tags;
unset resp.http.X-Powered-By;
unset resp.http.Server;
unset resp.http.X-Varnish;
unset resp.http.Via;
unset resp.http.Link;
}
sub vcl_synth {
if(req.method == "PURGE") {
set resp.http.Content-Type = "application/json";
if(req.http.X-Magento-Tags-Pattern) {
set resp.body = {"{ "invalidated": "} + resp.reason + {" }"};
} else {
set resp.body = {"{ "invalidated": 1 }"};
}
set resp.reason = "OK";
return(deliver);
}
}