Reverse Http Cache

Overview

A reverse http cache is a cache server placed before the web shop. If you are not familiar with http caching, please see our http cache concept. The reverse http cache needs the following capabilities to fully function with Shopware:
    Able to differentiate the request with multiple cookies
    Allow clearing the cache using a web request for a specific site or with / for all pages
In this guide, we will use Varnish as example for an http cache.

The example Setup with Varnish

This setup is compatible from Shopware version 6.4.
At first we need to activate the reverse proxy support in Shopware. To enable it we need to create a new file in config/packages/storefront.yaml
1
storefront:
2
csrf:
3
enabled: true
4
# The internal Shopware http cache replaces the csrf token on the fly. This can't be done in Reverse proxy. So we use ajax to get an csrf token
5
mode: ajax
6
reverse_proxy:
7
enabled: true
8
ban_method: "BAN"
9
# This needs to point to your varnish hosts
10
hosts: [ "http://varnish" ]
11
# Max parallel invalidations at same time for a single worker
12
max_parallel_invalidations: 3
13
# Redis Storage for the http cache tags
14
redis_url: "redis://redis"
Copied!
Also set SHOPWARE_HTTP_CACHE_ENABLED=1 in your .env file.
As Shopware is now prepared to work with a reverse proxy, we need to configure our Varnish too using a Shopware specific configuration. Below you can find an example Varnish configuration.
1
vcl 4.0;
2
​
3
import std;
4
​
5
# You should specify here all your app nodes and use round robin to select a backend
6
backend default {
7
.host = "<app-host>";
8
.port = "80";
9
}
10
​
11
# ACL for purgers IP. (This needs to contain app server ips)
12
acl purgers {
13
"127.0.0.1";
14
"localhost";
15
"::1";
16
}
17
​
18
sub vcl_recv {
19
# Mitigate httpoxy application vulnerability, see: https://httpoxy.org/
20
unset req.http.Proxy;
21
​
22
# Strip query strings only needed by browser javascript. Customize to used tags.
23
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)=") {
24
# see rfc3986#section-2.3 "Unreserved Characters" for regex
25
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\-\_\.\~]+&?", "");
26
}
27
set req.url = regsub(req.url, "(\?|\?&|&)quot;, "");
28
​
29
# Normalize query arguments
30
set req.url = std.querysort(req.url);
31
​
32
# Make sure that the client ip is forward to the client.
33
if (req.http.x-forwarded-for) {
34
set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
35
} else {
36
set req.http.X-Forwarded-For = client.ip;
37
}
38
​
39
# Handle BAN
40
if (req.method == "BAN") {
41
if (!client.ip ~ purgers) {
42
return (synth(405, "Method not allowed"));
43
}
44
​
45
ban("req.url ~ "+req.url);
46
return (synth(200, "BAN URLs containing (" + req.url + ") done."));
47
}
48
​
49
# Normalize Accept-Encoding header
50
# straight from the manual: https://www.varnish-cache.org/docs/3.0/tutorial/vary.html
51
if (req.http.Accept-Encoding) {
52
if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)quot;) {
53
# No point in compressing these
54
unset req.http.Accept-Encoding;
55
} elsif (req.http.Accept-Encoding ~ "gzip") {
56
set req.http.Accept-Encoding = "gzip";
57
} elsif (req.http.Accept-Encoding ~ "deflate") {
58
set req.http.Accept-Encoding = "deflate";
59
} else {
60
# unkown algorithm
61
unset req.http.Accept-Encoding;
62
}
63
}
64
​
65
if (req.method != "GET" &&
66
req.method != "HEAD" &&
67
req.method != "PUT" &&
68
req.method != "POST" &&
69
req.method != "TRACE" &&
70
req.method != "OPTIONS" &&
71
req.method != "DELETE") {
72
/* Non-RFC2616 or CONNECT which is weird. */
73
return (pipe);
74
}
75
​
76
# We only deal with GET and HEAD by default
77
if (req.method != "GET" && req.method != "HEAD") {
78
return (pass);
79
}
80
​
81
# Don't cache Authenticate & Authorization
82
if (req.http.Authenticate || req.http.Authorization) {
83
return (pass);
84
}
85
​
86
# Always pass these paths directly to php without caching
87
# Note: virtual URLs might bypass this rule (e.g. /en/checkout)
88
if (req.url ~ "^/(checkout|account|admin|api)(/.*)?quot;) {
89
return (pass);
90
}
91
​
92
return (hash);
93
}
94
​
95
sub vcl_hash {
96
# Consider Shopware http cache cookies
97
if (req.http.cookie ~ "sw-cache-hash=") {
98
hash_data("+context=" + regsub(req.http.cookie, "^.*?sw-cache-hash=([^;]*);*.*quot;, "\1"));
99
} elseif (req.http.cookie ~ "sw-currency=") {
100
hash_data("+currency=" + regsub(req.http.cookie, "^.*?sw-currency=([^;]*);*.*quot;, "\1"));
101
}
102
}
103
​
104
sub vcl_hit {
105
}
106
​
107
sub vcl_backend_response {
108
# Fix Vary Header in some cases
109
# https://www.varnish-cache.org/trac/wiki/VCLExampleFixupVary
110
if (beresp.http.Vary ~ "User-Agent") {
111
set beresp.http.Vary = regsub(beresp.http.Vary, ",? *User-Agent *", "");
112
set beresp.http.Vary = regsub(beresp.http.Vary, "^, *", "");
113
if (beresp.http.Vary == "") {
114
unset beresp.http.Vary;
115
}
116
}
117
​
118
# Respect the Cache-Control=private header from the backend
119
if (
120
beresp.http.Pragma ~ "no-cache" ||
121
beresp.http.Cache-Control ~ "no-cache" ||
122
beresp.http.Cache-Control ~ "private"
123
) {
124
set beresp.ttl = 0s;
125
set beresp.http.X-Cacheable = "NO:Cache-Control=private";
126
set beresp.uncacheable = true;
127
return (deliver);
128
}
129
​
130
# strip the cookie before the image is inserted into cache.
131
if (bereq.url ~ "\.(png|gif|jpg|swf|css|js|webp)quot;) {
132
unset beresp.http.set-cookie;
133
}
134
​
135
# Allow items to be stale if needed.
136
set beresp.grace = 6h;
137
​
138
# Save the bereq.url so bans work efficiently
139
set beresp.http.x-url = bereq.url;
140
set beresp.http.X-Cacheable = "YES";
141
​
142
return (deliver);
143
}
144
​
145
sub vcl_deliver {
146
## we don't want the client to cache
147
set resp.http.Cache-Control = "max-age=0, private";
148
​
149
# remove link header, if session is already started to save client resources
150
if (req.http.cookie ~ "session-") {
151
unset resp.http.Link;
152
}
153
​
154
# Set a cache header to allow us to inspect the response headers during testing
155
if (obj.hits > 0) {
156
unset resp.http.set-cookie;
157
set resp.http.X-Cache = "HIT";
158
} else {
159
set resp.http.X-Cache = "MISS";
160
}
161
​
162
set resp.http.X-Cache-Hits = obj.hits;
163
}
Copied!
To verify if it works, you can look for a new response header X-Cache in the http response. It shows you if it was a cache hit or miss.
Last modified 2mo ago