Last active
August 24, 2022 08:50
-
-
Save dvershinin/de91aa78a96ae9f6e8d2950b3e1463a4 to your computer and use it in GitHub Desktop.
Magento 2 VCL file for Varnish 4.x: supports multiple stores better, allows purging arbitrary URLs https://www.getpagespeed.com/web-apps/magento/varnish-vcl-magento-2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
vcl 4.0; | |
import std; | |
# The minimal Varnish version is 4.0 | |
# For SSL offloading, pass the following header in your proxy server or load balancer: 'X-Forwarded-Proto: https' | |
backend default { | |
.host = "localhost"; | |
.port = "8080"; | |
.first_byte_timeout = 600s; | |
.probe = { | |
.url = "/health_check.php"; | |
.timeout = 2s; | |
.interval = 5s; | |
.window = 10; | |
.threshold = 5; | |
} | |
} | |
acl purge { | |
"localhost"; | |
} | |
sub vcl_recv { | |
if (req.method == "PURGE") { | |
if (client.ip !~ purge) { | |
return (synth(405, "Method not allowed")); | |
} | |
if (!req.http.X-Magento-Tags-Pattern) { | |
return (purge); | |
} | |
# required: https://github.com/magento/magento2/issues/4871 | |
if (req.http.host && req.http.host != "" && req.http.host != "127.0.0.1") { | |
ban("obj.http.X-Host ~ " + req.http.host + " && obj.http.X-Magento-Tags ~ " + req.http.X-Magento-Tags-Pattern); | |
} else { | |
ban("obj.http.X-Magento-Tags ~ " + req.http.X-Magento-Tags-Pattern); | |
} | |
return (synth(200, "Purged")); | |
} | |
if (req.method != "GET" && | |
req.method != "HEAD" && | |
req.method != "PUT" && | |
req.method != "POST" && | |
req.method != "TRACE" && | |
req.method != "OPTIONS" && | |
req.method != "DELETE") { | |
/* Non-RFC2616 or CONNECT which is weird. */ | |
return (pipe); | |
} | |
# We only deal with GET and HEAD by default | |
if (req.method != "GET" && req.method != "HEAD") { | |
return (pass); | |
} | |
# Bypass shopping cart, checkout | |
if (req.url ~ "/checkout") { | |
return (pass); | |
} | |
# normalize url in case of leading HTTP scheme and domain | |
set req.url = regsub(req.url, "^http[s]?://", ""); | |
# collect all cookies | |
std.collect(req.http.Cookie); | |
# Remove all marketing get parameters to minimize the cache objects | |
if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=") { | |
set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=[-_A-z0-9+()%.]+&?", ""); | |
set req.url = regsub(req.url, "[?|&]+$", ""); | |
} | |
# static files are always cacheable. remove SSL flag and cookie | |
if (req.url ~ "^/(media|static)/.*\.(ico|css|js|jpg|jpeg|png|gif|tiff|bmp|mp3|ogg|svg|swf|woff|woff2|eot|ttf|otf)$") { | |
unset req.http.Https; | |
unset req.http.X-Forwarded-Proto; | |
unset req.http.Cookie; | |
} | |
# Authenticated GraphQL requests should not be cached by default | |
if (req.url ~ "/graphql" && req.http.Authorization ~ "^Bearer") { | |
return (pass); | |
} | |
return (hash); | |
} | |
sub vcl_hash { | |
if (req.http.cookie ~ "X-Magento-Vary=") { | |
hash_data(regsub(req.http.cookie, "^.*?X-Magento-Vary=([^;]+);*.*$", "\1")); | |
} | |
# For multi site configurations to not cache each other's content | |
if (req.http.host) { | |
hash_data(req.http.host); | |
} else { | |
hash_data(server.ip); | |
} | |
if (req.url ~ "/graphql") { | |
call process_graphql_headers; | |
} | |
# To make sure http users don't see ssl warning | |
if (req.http.X-Forwarded-Proto) { | |
hash_data(req.http.X-Forwarded-Proto); | |
} | |
} | |
sub process_graphql_headers { | |
if (req.http.Store) { | |
hash_data(req.http.Store); | |
} | |
if (req.http.Content-Currency) { | |
hash_data(req.http.Content-Currency); | |
} | |
} | |
sub vcl_backend_response { | |
set beresp.http.X-Host = bereq.http.host; | |
set beresp.grace = 3d; | |
if (beresp.http.content-type ~ "text") { | |
set beresp.do_esi = true; | |
} | |
if (bereq.url ~ "\.js$" || beresp.http.content-type ~ "text") { | |
set beresp.do_gzip = true; | |
} | |
# cache only successfully responses and 404s | |
if (beresp.status != 200 && beresp.status != 404) { | |
set beresp.ttl = 0s; | |
set beresp.uncacheable = true; | |
return (deliver); | |
} elsif (beresp.http.Cache-Control ~ "private") { | |
set beresp.uncacheable = true; | |
set beresp.ttl = 86400s; | |
return (deliver); | |
} | |
if (beresp.http.X-Magento-Debug) { | |
set beresp.http.X-Magento-Cache-Control = beresp.http.Cache-Control; | |
} | |
# validate if we need to cache it and prevent from setting cookie | |
# images, css and js are cacheable by default so we have to remove cookie also | |
if (beresp.ttl > 0s && (bereq.method == "GET" || bereq.method == "HEAD")) { | |
unset beresp.http.set-cookie; | |
if (bereq.url !~ "\.(ico|css|js|jpg|jpeg|png|gif|tiff|bmp|gz|tgz|bz2|tbz|mp3|ogg|svg|swf|woff|woff2|eot|ttf|otf)(\?|$)") { | |
set beresp.http.Pragma = "no-cache"; | |
set beresp.http.Expires = "-1"; | |
set beresp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0"; | |
set beresp.grace = 1m; | |
} | |
} | |
# "Microcache" for search | |
if (bereq.url ~ "/catalogsearch") { | |
set beresp.ttl = 30m; | |
} | |
# If page is not cacheable then bypass varnish for 2 minutes as Hit-For-Pass | |
if (beresp.ttl <= 0s || | |
beresp.http.Surrogate-control ~ "no-store" || | |
(!beresp.http.Surrogate-Control && beresp.http.Vary == "*")) { | |
# Mark as Hit-For-Pass for the next 2 minutes | |
set beresp.ttl = 120s; | |
set beresp.uncacheable = true; | |
} | |
return (deliver); | |
} | |
sub vcl_deliver { | |
if (resp.http.X-Magento-Debug) { | |
if (resp.http.x-varnish ~ " ") { | |
set resp.http.X-Magento-Cache-Debug = "HIT"; | |
} else { | |
set resp.http.X-Magento-Cache-Debug = "MISS"; | |
} | |
} else { | |
unset resp.http.Age; | |
} | |
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_hit { | |
if (obj.ttl >= 0s) { | |
# Hit within TTL period | |
return (deliver); | |
} | |
if (std.healthy(req.backend_hint)) { | |
if (obj.ttl + 259200s > 0s) { | |
# Hit after TTL expiration, but within grace period | |
set req.http.grace = "normal (healthy server)"; | |
return (deliver); | |
} else { | |
# Hit after TTL and grace expiration | |
return (fetch); | |
} | |
} else { | |
# server is not healthy, retrieve from cache | |
set req.http.grace = "unlimited (unhealthy server)"; | |
return (deliver); | |
} | |
} |
@ericrisler sure, thanks.
Updated the code so that arbitrary URLs can be purged also.
Removed Accept-Encoding
logic that is not required with Varnish 4.
obj.http.X-Host " + req.http.host + " && obj.http.X-Magento-Tags what do these do exactly?
My host removed req.http.host + " && obj.http.X-Magento-Tags from the vcl because I was having issues of dev site and production site on same server flushing cache of the other.
However, now I'm trying to use a firewall provider and no matter the cache host or acl IPs added to the magento configuration I can't get magento to purge cache. Works fine the way he did it prior to firewall. I'm 99% it's an issue with ACL not being honored.
Those lines are for clearing specific site cache rather than the default (clearing all).
So I can’t see how removing it be solution against the problem that it’s meant to fix.
You might have some fancy setup. Firewall has nothing to do with Varnish unless you build a CDN consisting of many Varnish instances. In standard setup Varnish listens on loopback interface 127.0.0.1 and Magento talks to it for cache purge. Where do you see firewall interfering in that kind of scenario?
…Sent from my iPhone
On 23 Aug 2019, at 16:12, Scott Noroozi ***@***.***> wrote:
obj.http.X-Host " + req.http.host + " && obj.http.X-Magento-Tags what do these do exactly?
My host removed req.http.host + " && obj.http.X-Magento-Tags from the vcl because I was having issues of dev site and production site on same server flushing cache of the other.
However, now I'm trying to use a firewall provider and no matter the cache host or acl IPs added to the magento configuration I can't get magento to purge cache. Works fine the way he did it prior to firewall. I'm 99% it's an issue with ACL not being honored.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub, or mute the thread.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Line 24 causes a compile error - I think yit should be
ban("obj.http.X-Host ~ " + req.http.host " && obj.http.X-Magento-Tags ~ " + req.http.X-Magento-Tags-Pattern);
removes the"
after the&&
Thanks for config example.