Shopware HTTP Cache mit Varnish
Für die Konfiguration von Varnish stellt Shopware eine vollständige Konfiguration in den Shopware Developer Docs bereit:
Trusted Proxies
Die Verwendung von Symfony und Varnish stellt in den meisten Fällen kein Problem dar. Wenn jedoch eine Anfrage durch einen Proxy läuft, werden bestimmte Anfragedaten entweder mit dem Standard-Header Forwarded
oder X-Forwarded
gesendet. Anstatt beispielsweise den Header REMOTE_ADDR
zu lesen (der jetzt die IP-Adresse Ihres Reverse-Proxys ist), wird die tatsächliche IP-Adresse des Benutzers gespeichert.
Wenn Sie Symfony nicht so konfigurieren, dass es nach diesen Headern sucht, werden Sie falsche Informationen über die IP-Adresse des Clients erhalten. Unabhängig davon, ob sich der Client über https verbindet oder nicht, werden der Port des Clients und der Hostname abgefragt.
Beispiel Konfiguration über .env
:
TRUSTED_PROXIES=127.0.0.1,127.0.0.2,10.20.1.1/32,10.20.0.1/32,10.20.0.2/32
In dieser Beispiel-Konfiguration werden die beiden Varnish-Instanzen 10.20.0.1
und 10.20.0.2
als Proxy autorisiert.
Mehr Informationen hierzu finden Sie unter:
Reverse Proxy Konfiguration
Damit Shopware 6 den korrekten Cache-Control
Header setzt, ist die Konfiguration des Reverse Proxies erforderlich. Mithilfe der folgenden Konfiguration kann sichergestellt werden, dass Shopware anstelle des Cache-Control
Header Wertes private, no-cache
den Wert public, must-revalidate
setzt und die Caches entsprechend in Varnish via BAN
Requests invalidiert.
Erzeugen Sie hierzu folgende Datei:
config/packages/storefront.yaml
Bis Shopware 6.5.X.X
storefront:
reverse_proxy:
enabled: true
ban_method: "BAN"
# Varnish hosts (IP:Port) [Default Port: 80]
hosts: [ "http://10.20.X.X", "http://10.20.X.X" ]
# Additional Headers for BAN Requests (E.G. Overwrite Host Header)
ban_headers:
Host: "www.creoline-demo.com"
# Max parallel invalidations at same time for a single worker
max_parallel_invalidations: 5
# Redis (Default DB: 0, Use /X for another database)
redis_url: "redis://10.20.X.X:6379/0"
Ab Shopware 6.6.0.0
shopware:
http_cache:
reverse_proxy:
enabled: true
ban_method: "BAN"
# Varnish hosts (IP:Port) [Default Port: 80]
hosts: [ "http://10.20.X.X", "http://10.20.X.X" ]
# Additional Headers for BAN Requests (E.G. Overwrite Host Header)
ban_headers:
Host: "www.creoline-demo.com"
# Max parallel invalidations at same time for a single worker
max_parallel_invalidations: 5
# Redis (Default DB: 0, Use /X for another database)
redis_url: "redis://10.20.X.X:6379/0"
Achtung: Shopware verwendet seit der Version 6.6.0.0 ein anderes YAML-Schema. Sofern Sie Shopware bereits mit Varnish in einer früheren Version einsetzen, sollte das neue Schema bereits vor dem Upgrade entsprechend hinterlegt werden.
Bitte stellen Sie sicher, dass Sie die korrekten IP-Adressen der Varnish-Instanzen inklusive Angabe der HTTP Ports angeben, sofern dieser vom Standard Port 80 abweicht. In der Standard-Konfiguration des creoline Varnish-Servers ist hier der HTTP Port 80
konfiguriert. Sofern Sie Varnish eigenständig installiert haben, ist der Standard Port 6081
.
Beachten Sie, dass der HTTP Host
Header im BAN
Request im Standard den IP-Adressen bzw. den Hostnamen der angegebenen hosts
entspricht. Mithilfe der Angabe von ban_headers
kann der Host
Header entsprechend überschrieben werden, sodass der korrekte Cache-Key in Varnish ermittelt werden kann.
Varnish Konfiguration
Die Varnish-Konfiguration kann direkt über das Konfigurationsmodul im Kundencenter editiert und ausgerollt werden. Navigieren Sie zu Server → Ihr Server → Konfigurationsdateien → Varnish Config, um die Varnish Konfiguration anzupassen.
Bitte beachten Sie, dass das Speichern der Änderungen der Varnish-Konfiguration umgehend einen Configtest mit einem anschließenden Reload der Varnish-Instanz auslöst. Sollten Sie Varnish inital konfigurieren, empfehlen wir eine Test-Instanz zur Evaluierung der korrekten Konfiguration.
Beispiel Konfiguration mit Soft-Purge:
vcl 4.0;
import std;
import purge;
# You should specify here all your app nodes and use round robin to select a backend
backend default {
.host = "<app-host>";
.port = "80";
}
# ACL for purgers IP. (This needs to contain app server ips)
acl purgers {
"127.0.0.1";
"localhost";
"::1";
}
sub vcl_recv {
# Mitigate httpoxy application vulnerability, see: https://httpoxy.org/
unset req.http.Proxy;
# Strip query strings only needed by browser javascript. Customize to used tags.
if (req.url ~ "(\?|&)(pk_campaign|piwik_campaign|pk_kwd|piwik_kwd|pk_keyword|pixelId|kwid|kw|adid|chl|dv|nk|pa|camid|adgid|cx|ie|cof|siteurl|utm_[a-z]+|_ga|gclid)=") {
# see rfc3986#section-2.3 "Unreserved Characters" for regex
set req.url = regsuball(req.url, "(pk_campaign|piwik_campaign|pk_kwd|piwik_kwd|pk_keyword|pixelId|kwid|kw|adid|chl|dv|nk|pa|camid|adgid|cx|ie|cof|siteurl|utm_[a-z]+|_ga|gclid)=[A-Za-z0-9\-\_\.\~]+&?", "");
}
set req.url = regsub(req.url, "(\?|\?&|&)$", "");
# Normalize query arguments
set req.url = std.querysort(req.url);
# Make sure that the client ip is forward to the client.
if (req.http.x-forwarded-for) {
set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
} else {
set req.http.X-Forwarded-For = client.ip;
}
# Handle BAN
if (req.method == "BAN") {
if (!client.ip ~ purgers) {
return (synth(405, "Method not allowed"));
}
return (hash);
}
# Normalize Accept-Encoding header
# straight from the manual: https://www.varnish-cache.org/docs/3.0/tutorial/vary.html
if (req.http.Accept-Encoding) {
if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") {
# No point in compressing these
unset req.http.Accept-Encoding;
} elsif (req.http.Accept-Encoding ~ "gzip") {
set req.http.Accept-Encoding = "gzip";
} elsif (req.http.Accept-Encoding ~ "deflate") {
set req.http.Accept-Encoding = "deflate";
} else {
# unknown algorithm
unset req.http.Accept-Encoding;
}
}
if (req.method != "GET" &&
req.method != "HEAD" &&
req.method != "PUT" &&
req.method != "POST" &&
req.method != "TRACE" &&
req.method != "OPTIONS" &&
req.method != "PATCH" &&
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);
}
# Don't cache Authenticate & Authorization
if (req.http.Authenticate || req.http.Authorization) {
return (pass);
}
# Always pass these paths directly to php without caching
# Note: virtual URLs might bypass this rule (e.g. /en/checkout)
if (req.url ~ "^/(checkout|account|admin|api)(/.*)?$") {
return (pass);
}
return (hash);
}
sub vcl_hash {
# Consider Shopware HTTP cache cookies
if (req.http.cookie ~ "sw-cache-hash=") {
hash_data("+context=" + regsub(req.http.cookie, "^.*?sw-cache-hash=([^;]*);*.*$", "\1"));
} elseif (req.http.cookie ~ "sw-currency=") {
hash_data("+currency=" + regsub(req.http.cookie, "^.*?sw-currency=([^;]*);*.*$", "\1"));
}
}
sub vcl_hit {
if (req.method == "BAN") {
call soft_purge_page;
}
# Consider client states for response headers
if (req.http.cookie ~ "sw-states=") {
set req.http.states = regsub(req.http.cookie, "^.*?sw-states=([^;]*);*.*$", "\1");
if (req.http.states ~ "logged-in" && obj.http.sw-invalidation-states ~ "logged-in" ) {
return (pass);
}
if (req.http.states ~ "cart-filled" && obj.http.sw-invalidation-states ~ "cart-filled" ) {
return (pass);
}
}
}
sub vcl_miss {
if (req.method == "BAN") {
call soft_purge_page;
}
}
sub vcl_backend_response {
# Fix Vary Header in some cases
# https://www.varnish-cache.org/trac/wiki/VCLExampleFixupVary
if (beresp.http.Vary ~ "User-Agent") {
set beresp.http.Vary = regsub(beresp.http.Vary, ",? *User-Agent *", "");
set beresp.http.Vary = regsub(beresp.http.Vary, "^, *", "");
if (beresp.http.Vary == "") {
unset beresp.http.Vary;
}
}
# Respect the Cache-Control=private header from the backend
if (
beresp.http.Pragma ~ "no-cache" ||
beresp.http.Cache-Control ~ "no-cache" ||
beresp.http.Cache-Control ~ "private"
) {
set beresp.ttl = 0s;
set beresp.http.X-Cacheable = "NO:Cache-Control=private";
set beresp.uncacheable = true;
return (deliver);
}
# strip the cookie before the image is inserted into cache.
if (bereq.url ~ "\.(png|gif|jpg|swf|css|js|webp)$") {
unset beresp.http.set-cookie;
}
# Allow items to be stale if needed.
set beresp.grace = 6h;
# Save the bereq.url so bans work efficiently
set beresp.http.x-url = bereq.url;
set beresp.http.X-Cacheable = "YES";
# Remove the exact PHP Version from the response for more security
unset beresp.http.x-powered-by;
return (deliver);
}
sub vcl_deliver {
## we don't want the client to cache
set resp.http.Cache-Control = "max-age=0, private";
# remove link header, if session is already started to save client resources
if (req.http.cookie ~ "session-") {
unset resp.http.Link;
}
# Set a cache header to allow us to inspect the response headers during testing
if (obj.hits > 0) {
unset resp.http.set-cookie;
set resp.http.X-Cache = "HIT";
if (obj.ttl <= 0s && obj.grace > 0s) {
set resp.http.X-Cache = "STALE";
}
} else {
set resp.http.X-Cache = "MISS";
}
# Remove the exact PHP Version from the response for more security (e.g. 404 pages)
unset resp.http.x-powered-by;
# Remove additional information about Varnish
unset resp.http.via;
unset resp.http.x-varnish;
# invalidation headers are only for internal use
unset resp.http.sw-invalidation-states;
set resp.http.X-Cache-Hits = obj.hits;
set resp.http.X-Cache-Lifetime = obj.ttl;
}
sub soft_purge_page {
# See https://docs.varnish-software.com/varnish-cache-plus/vmods/purge/ for all possible options
set req.http.purged = purge.soft(ttl = 0s, grace = 300s, keep = 3600s);
return (synth(200));
}
HTTP-Cache für angemeldete Benutzer oder Besucher mit Warenkörben
Der Shopware HTTP-Cache steht in der Standard-Einstellung nur für nicht angemeldete Benutzer und Besucher ohne Warenkorb Inhalte zur Verfügung. Sofern Sie keine Anpassungen für angemeldete Benutzer verwenden, kann der HTTP-Cache auch für angemeldete Benutzer oder Besucher mit Warenkörben aktiviert werden.
# config/packages/prod/shopware.yaml
shopware:
cache:
invalidation:
http_cache: []