Skip to content

Commit bc6cd08

Browse files
committed
Varnish Enterprise template for Magento 2:
- Uses vmod_udo & vmod_activedns for dynamic backends, DNS-based loadbalancing & DNS-based service discovery - Uses vmod_urlplus to easily remove marketing parameters from the query string - Both vmod_urlplus & vmod_uri to strip off port numbers and alphabeticaly sort query string parameters - Sets the X-Forwarded-Proto & SSL-Offloaded header - Leverages vmod_ykey to perform true tag-based cache invalidation with soft purge support - Respects Magento caching logic to ensure compatibility with Magento 2
0 parents  commit bc6cd08

File tree

2 files changed

+369
-0
lines changed

2 files changed

+369
-0
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Magento Varnish Configuration
2+
=============================
3+
4+
This repository contains Magento Varnish (VCL) configurations from Varnish Software.

default.vcl

+365
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
1+
# VCL version marker. Has no correlation with the Varnish version. VCL version 4.1 requires Varnish 6 or higher
2+
vcl 4.1;
3+
4+
# Import Varnish modules
5+
import uri;
6+
import urlplus;
7+
import cookieplus;
8+
import std;
9+
import tls;
10+
import ykey;
11+
import activedns;
12+
import udo;
13+
14+
# Access Control List (ACL) for cache invalidation access
15+
acl purge {
16+
"localhost";
17+
"127.0.0.1";
18+
"::1";
19+
"172.18.0.0/24"; # Change this and add the right IP addresses & CIDRs
20+
}
21+
22+
# Probe template, used by UDO
23+
probe health {
24+
.url = "/health_check.php";
25+
.timeout = 2s;
26+
.interval = 5s;
27+
.window = 10;
28+
.threshold = 5;
29+
}
30+
31+
# Backend template, used by UDO
32+
backend default {
33+
.host = "0.0.0.0";
34+
.ssl = 1;
35+
.ssl_verify_host = 0;
36+
.ssl_verify_peer = 0;
37+
.ssl_sni = 1;
38+
.host_header = "example.com"; # Change this and add the right host header
39+
.first_byte_timeout = 600s;
40+
}
41+
42+
# Initialize DNS-based dynamic backends
43+
sub vcl_init {
44+
# Initialize DNS group with the right hostname and port number
45+
# Supports SRV, A and AAAA records
46+
new group = activedns.dns_group("magento.example.com:443"); # Change this and add the right backend hostname and port number
47+
# Only allow IPv4 addresses
48+
group.set_ipv_rule(ipv4);
49+
# Ignore DNS TTL from the DNS server and force it to 5 seconds
50+
group.set_ttl_rule(force);
51+
group.set_ttl(5s);
52+
# Assign backend and health probe templates
53+
# The DNS group will override the host and port number
54+
group.set_backend_template(default);
55+
group.set_probe_template(health);
56+
57+
# Initiliaze a Unified Director Object (UDO)
58+
new magento = udo.director();
59+
# If the DNS record returns multiple values, perform random loadbalancing based on those values
60+
magento.set_type(random);
61+
# Subscribe to the DNS group to capture potential DNS changes
62+
magento.subscribe(group.get_tag());
63+
}
64+
65+
sub vcl_backend_fetch {
66+
# Assign a backend dynamically using the Unified Director Object
67+
set bereq.backend = magento.backend();
68+
}
69+
70+
sub vcl_backend_error {
71+
# Capture potential backend failures and retry on another server
72+
return (retry);
73+
}
74+
75+
sub vcl_recv {
76+
# Remove all marketing get parameters to minimize the cache objects
77+
# You can add additional parameters that occur in your setup
78+
urlplus.query_delete("_branch_match_id");
79+
urlplus.query_delete("srsltid");
80+
urlplus.query_delete("_bta_c");
81+
urlplus.query_delete("_bta_tid");
82+
urlplus.query_delete("_ga");
83+
urlplus.query_delete("_gl");
84+
urlplus.query_delete("_ke");
85+
urlplus.query_delete("_kx");
86+
urlplus.query_delete("campid");
87+
urlplus.query_delete("cof");
88+
urlplus.query_delete("customid");
89+
urlplus.query_delete("cx");
90+
urlplus.query_delete("dclid");
91+
urlplus.query_delete("dm_i");
92+
urlplus.query_delete("ef_id");
93+
urlplus.query_delete("epik");
94+
urlplus.query_delete("fbclid");
95+
urlplus.query_delete("gad_source");
96+
urlplus.query_delete("gbraid");
97+
urlplus.query_delete("gclid");
98+
urlplus.query_delete("gclsrc");
99+
urlplus.query_delete("gdffi");
100+
urlplus.query_delete("gdfms");
101+
urlplus.query_delete("gdftrk");
102+
urlplus.query_delete("hsa_acc");
103+
urlplus.query_delete("hsa_ad");
104+
urlplus.query_delete("hsa_cam");
105+
urlplus.query_delete("hsa_grp");
106+
urlplus.query_delete("hsa_kw");
107+
urlplus.query_delete("hsa_mt");
108+
urlplus.query_delete("hsa_net");
109+
urlplus.query_delete("hsa_src");
110+
urlplus.query_delete("hsa_tgt");
111+
urlplus.query_delete("hsa_ver");
112+
urlplus.query_delete("ie");
113+
urlplus.query_delete("igshid");
114+
urlplus.query_delete("irclickid");
115+
urlplus.query_delete("matomo_campaign");
116+
urlplus.query_delete("matomo_cid");
117+
urlplus.query_delete("matomo_content");
118+
urlplus.query_delete("matomo_group");
119+
urlplus.query_delete("matomo_keyword");
120+
urlplus.query_delete("matomo_medium");
121+
urlplus.query_delete("matomo_placement");
122+
urlplus.query_delete("matomo_source");
123+
urlplus.query_delete("mc_cid");
124+
urlplus.query_delete("mc_eid");
125+
urlplus.query_delete("mkcid");
126+
urlplus.query_delete("mkevt");
127+
urlplus.query_delete("mkrid");
128+
urlplus.query_delete("mkwid");
129+
urlplus.query_delete("msclkid");
130+
urlplus.query_delete("mtm_campaign");
131+
urlplus.query_delete("mtm_cid");
132+
urlplus.query_delete("mtm_content");
133+
urlplus.query_delete("mtm_group");
134+
urlplus.query_delete("mtm_keyword");
135+
urlplus.query_delete("mtm_medium");
136+
urlplus.query_delete("mtm_placement");
137+
urlplus.query_delete("mtm_source");
138+
urlplus.query_delete("nb_klid");
139+
urlplus.query_delete("ndclid");
140+
urlplus.query_delete("origin");
141+
urlplus.query_delete("pcrid");
142+
urlplus.query_delete("piwik_campaign");
143+
urlplus.query_delete("piwik_keyword");
144+
urlplus.query_delete("piwik_kwd");
145+
urlplus.query_delete("pk_campaign");
146+
urlplus.query_delete("pk_keyword");
147+
urlplus.query_delete("pk_kwd");
148+
urlplus.query_delete("redirect_log_mongo_id");
149+
urlplus.query_delete("redirect_mongo_id");
150+
urlplus.query_delete("rtid");
151+
urlplus.query_delete("sb_referer_host");
152+
urlplus.query_delete("ScCid");
153+
urlplus.query_delete("si");
154+
urlplus.query_delete("siteurl");
155+
urlplus.query_delete("s_kwcid");
156+
urlplus.query_delete("sms_click");
157+
urlplus.query_delete("sms_source");
158+
urlplus.query_delete("sms_uph");
159+
urlplus.query_delete("toolid");
160+
urlplus.query_delete("trk_contact");
161+
urlplus.query_delete("trk_module");
162+
urlplus.query_delete("trk_msg");
163+
urlplus.query_delete("trk_sid");
164+
urlplus.query_delete("ttclid");
165+
urlplus.query_delete("twclid");
166+
urlplus.query_delete("utm_campaign");
167+
urlplus.query_delete("utm_content");
168+
urlplus.query_delete("utm_creative_format");
169+
urlplus.query_delete("utm_id");
170+
urlplus.query_delete("utm_marketing_tactic");
171+
urlplus.query_delete("utm_medium");
172+
urlplus.query_delete("utm_source");
173+
urlplus.query_delete("utm_source_platform");
174+
urlplus.query_delete("utm_term");
175+
urlplus.query_delete("wbraid");
176+
urlplus.query_delete("yclid");
177+
urlplus.query_delete("zanpid");
178+
179+
# Writes changes back to the URL and sorts query string parameters alphabetically
180+
urlplus.write();
181+
# Remove port number from host header
182+
uri.set_port();
183+
uri.write();
184+
185+
# Remove the proxy header to mitigate the httpoxy vulnerability
186+
# See https://httpoxy.org/
187+
unset req.http.proxy;
188+
189+
# Add X-Forwarded-Proto and Ssl-Offloaded header value based on the protocol
190+
if(req.http.Ssl-Offloaded == "1") {
191+
set req.http.X-Forwarded-Proto = "https";
192+
} elseif (!req.http.Ssl-Offloaded && req.http.X-Forwarded-Proto == "https") {
193+
set req.http.Ssl-Offloaded = "1";
194+
} elseif(!req.http.Ssl-Offloaded && !req.http.X-Forwarded-Proto && tls.is_tls()) {
195+
set req.http.X-Forwarded-Proto = "https";
196+
set req.http.Ssl-Offloaded = "1";
197+
} else {
198+
set req.http.X-Forwarded-Proto = "http";
199+
set req.http.Ssl-Offloaded = "0";
200+
}
201+
202+
# Reduce grace to the configured setting if the backend is healthy
203+
# In case of an unhealthy backend, the original grace is used
204+
if (std.healthy(req.backend_hint)) {
205+
set req.grace = 300s;
206+
}
207+
208+
# Intercept purge requests from Magento to perform cache invalidations
209+
if (req.method == "PURGE") {
210+
# Only allow clients that match the ACL
211+
if (client.ip !~ purge) {
212+
return (synth(405, "Method not allowed"));
213+
}
214+
# Perform a regular URL-based purge when X-Magento-Tags is not set
215+
if (!req.http.X-Magento-Tags-Pattern) {
216+
return (purge);
217+
}
218+
# Remove regex content from X-Magento-Tags-Pattern and keep the actual comma-separated tags
219+
set req.http.X-Key-Purge = regsuball(req.http.X-Magento-Tags-Pattern, "[\(\)\^\$]", "");
220+
set req.http.X-Key-Purge = regsuball(req.http.X-Key-Purge, "[,\|]", " ");
221+
set req.http.X-Key-Purge = regsuball(req.http.X-Key-Purge, "\.\*", "all");
222+
# Soft purge objects that match the tags
223+
set req.http.n-purged = ykey.purge_header(req.http.X-Key-Purge, " ", true);
224+
return (synth(200, "Purged " + req.http.n-purged + " objects"));
225+
}
226+
227+
# If the HTTP request method doesn't match one of these, it's probably not a valid HTTP request
228+
# Send the content to the backend and abandon any notion of HTTP and HTTP caching
229+
if (req.method != "GET" &&
230+
req.method != "HEAD" &&
231+
req.method != "PUT" &&
232+
req.method != "POST" &&
233+
req.method != "PATCH" &&
234+
req.method != "TRACE" &&
235+
req.method != "OPTIONS" &&
236+
req.method != "DELETE") {
237+
return (pipe);
238+
}
239+
240+
# We only cache GET and HEAD requests
241+
if (req.method != "GET" && req.method != "HEAD") {
242+
return (pass);
243+
}
244+
245+
# Bypass health check requests
246+
if (req.url ~ "^/(pub/)?(health_check.php)$") {
247+
return (pass);
248+
}
249+
250+
# Collapse multiple cookie headers into one
251+
std.collect(req.http.Cookie);
252+
253+
# Static files caching
254+
if (req.url ~ "^/(pub/)?(media|static)/") {
255+
# If you decide not to store static content in the cache, just uncomment the next line
256+
#return (pass);
257+
258+
# If you decide to cache static files, remove cookies
259+
unset req.http.Cookie;
260+
# Remove X-Forwarded-Proto to reduce cache variations
261+
unset req.http.X-Forwarded-Proto;
262+
}
263+
264+
# Don't cache the authenticated GraphQL requests
265+
if (req.url ~ "/graphql" && req.http.Authorization ~ "^Bearer") {
266+
return (pass);
267+
}
268+
269+
return (hash);
270+
}
271+
272+
sub vcl_hash {
273+
# Create a cache variation for the GraphQL requests that based on the X-Magento-Vary cookie
274+
if (req.url !~ "/graphql") {
275+
hash_data(cookieplus.get("X-Magento-Vary"));
276+
}
277+
278+
# Store HTTP and HTTPS content separately
279+
hash_data(req.http.X-Forwarded-Proto);
280+
281+
if (req.url ~ "/graphql") {
282+
if (req.http.X-Magento-Cache-Id) {
283+
hash_data(req.http.X-Magento-Cache-Id);
284+
} else {
285+
# if no X-Magento-Cache-Id (which already contains Store and Currency) is not set, use the HTTP headers
286+
hash_data(req.http.Store);
287+
hash_data(req.http.Content-Currency);
288+
}
289+
}
290+
}
291+
292+
sub vcl_backend_response {
293+
# Register X-Magento-Tags header with Ykey
294+
ykey.add_header(beresp.http.X-Magento-Tags, sep=",");
295+
# Associate every object with the "all" key, for when a full cache purge takes place
296+
ykey.add_key("all");
297+
298+
# Serve stale content for three days after object expiration
299+
# Perform asynchronous revalidation while stale content is served
300+
set beresp.grace = 3d;
301+
302+
# All text-based content can be parsed as ESI
303+
if (beresp.http.content-type ~ "text") {
304+
set beresp.do_esi = true;
305+
}
306+
307+
# Allow GZIP compression on all JavaScript files and all text-based content
308+
if (urlplus.get_extension() == "js" || beresp.http.content-type ~ "text") {
309+
set beresp.do_gzip = true;
310+
}
311+
312+
# Add debug headers
313+
if (beresp.http.X-Magento-Debug) {
314+
set beresp.http.X-Magento-Cache-Control = beresp.http.Cache-Control;
315+
}
316+
317+
# Only cache HTTP 200 and HTTP 404 responses
318+
if (beresp.status != 200 && beresp.status != 404) {
319+
set beresp.ttl = 120s;
320+
set beresp.uncacheable = true;
321+
return (deliver);
322+
}
323+
324+
# Don't cache if the request cache ID doesn't match the response cache ID for graphql requests
325+
if (bereq.url ~ "/graphql" && bereq.http.X-Magento-Cache-Id && bereq.http.X-Magento-Cache-Id != beresp.http.X-Magento-Cache-Id) {
326+
set beresp.ttl = 120s;
327+
set beresp.uncacheable = true;
328+
return (deliver);
329+
}
330+
331+
# Remove the Set-Cookie header for cacheable content
332+
# Only for HTTP GET & HTTP HEAD requests
333+
if (beresp.ttl > 0s && (bereq.method == "GET" || bereq.method == "HEAD")) {
334+
unset beresp.http.Set-Cookie;
335+
}
336+
}
337+
338+
sub vcl_deliver {
339+
if (obj.uncacheable) {
340+
set resp.http.X-Magento-Cache-Debug = "UNCACHEABLE";
341+
} else if (obj.hits) {
342+
set resp.http.X-Magento-Cache-Debug = "HIT";
343+
} else {
344+
set resp.http.X-Magento-Cache-Debug = "MISS";
345+
}
346+
347+
# Not letting browser cache non-static files.
348+
if (resp.http.Cache-Control !~ "private" && req.url !~ "^/(pub/)?(media|static)/") {
349+
set resp.http.Pragma = "no-cache";
350+
set resp.http.Expires = "-1";
351+
set resp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
352+
}
353+
354+
if (!resp.http.X-Magento-Debug) {
355+
unset resp.http.X-Magento-Cache-Debug;
356+
unset resp.http.Age;
357+
}
358+
unset resp.http.X-Magento-Debug;
359+
unset resp.http.X-Magento-Tags;
360+
unset resp.http.X-Powered-By;
361+
unset resp.http.Server;
362+
unset resp.http.X-Varnish;
363+
unset resp.http.Via;
364+
unset resp.http.Link;
365+
}

0 commit comments

Comments
 (0)