Skip to content

Instantly share code, notes, and snippets.

@mklooss
Last active March 24, 2025 09:01
Show Gist options
  • Select an option

  • Save mklooss/fb373cdb7e9b47312811c1e04de3d7f7 to your computer and use it in GitHub Desktop.

Select an option

Save mklooss/fb373cdb7e9b47312811c1e04de3d7f7 to your computer and use it in GitHub Desktop.
shopware 6 varnish sample config
apt update
apt install varnish varnish-modules -y
server {
listen 127.0.0.1:8080;
index index.php index.html;
server_name varnish.default.tld;
root /home/USER/htdocs/public/;
location /recovery/install {
index index.php;
try_files $uri /recovery/install/index.php$is_args$args;
}
location /recovery/update/ {
location /recovery/update/assets {
}
if (!-e $request_filename){
rewrite . /recovery/update/index.php last;
}
}
location / {
try_files $uri /index.php$is_args$args;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include /etc/nginx/fastcgi_params;
fastcgi_param HTTP_PROXY "";
fastcgi_buffers 8 16k;
fastcgi_buffer_size 32k;
client_max_body_size 24M;
client_body_buffer_size 128k;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/var/run/php7.4-fpm.sock;
http2_push_preload on;
}
}
server {
listen 80;
listen [::]:80;
server_name varnish.default.tld;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name varnish.default.tld;
# Server certificate and key.
ssl_certificate /opt/cert/self/server.cert;
ssl_certificate_key /opt/cert/self/server.key;
location = /robots.txt {
add_header Content-Type text/plain;
return 200 "User-agent: *\nDisallow: /\n";
}
location / {
# Forward request to Varnish.
proxy_pass http://127.0.0.1:6081;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_hide_header Via;
proxy_redirect off;
}
}
# config/packages/framework.yaml
---
framework:
trusted_proxies: '%env(TRUSTED_PROXIES)%'
# config/packages/prod/varnish.yaml
---
shopware:
http_cache:
reverse_proxy:
enabled: true
use_varnish_xkey: true
hosts:
- 127.0.0.1:6081
# .env in Shopware dir
SHOPWARE_HTTP_CACHE_ENABLED=1
TRUSTED_PROXIES=127.0.0.1
[Unit]
Description=Varnish Cache, a high-performance HTTP accelerator
Documentation=https://www.varnish-cache.org/docs/ man:varnishd
[Service]
Type=simple
# Maximum number of open files (for ulimit -n)
LimitNOFILE=131072
# Locked shared memory - should suffice to lock the shared memory log
# (varnishd -l argument)
# Default log size is 80MB vsl + 1M vsm + header -> 82MB
# unit is bytes
LimitMEMLOCK=85983232
Environment=LD_PRELOAD=/opt/lib/libmimalloc.so
ExecStart=/usr/sbin/varnishd \
-j unix,user=vcache \
-F \
-a :6081 \
-T localhost:6082 \
-p thread_pool_min=10 \
-p thread_pool_max=500 \
-p http_resp_hdr_len=65536 \
-p http_resp_size=98304 \
-p workspace_backend=320k \
-p workspace_client=320k \
-f /etc/varnish/default.vcl \
-S /etc/varnish/secret \
-s Cache=malloc,12g \
-s Transient=malloc,512m
ExecReload=/usr/share/varnish/varnishreload
ProtectSystem=full
ProtectHome=true
PrivateTmp=true
PrivateDevices=true
[Install]
WantedBy=multi-user.target
vcl 4.1;
import std;
import xkey;
import cookie;
# Specify your app nodes here. Use round-robin balancing to add more than one.
backend default {
.host = "127.0.0.1";
.port = "8080";
}
# ACL for purgers IP. (This needs to contain app server ips)
acl purgers {
"127.0.0.1";
"localhost";
"::1";
}
sub vcl_recv {
if (req.restarts > 0) {
set req.hash_always_miss = true;
}
# Handle PURGE
if (req.method == "PURGE") {
if (client.ip !~ purgers) {
return (synth(403, "Forbidden"));
}
if (req.http.xkey) {
set req.http.n-gone = xkey.purge(req.http.xkey);
return (synth(200, "Invalidated "+req.http.n-gone+" objects"));
} else {
return (purge);
}
}
# Handle BAN
if (req.method == "BAN") {
if (!client.ip ~ purgers) {
return (synth(403, "Forbidden"));
}
ban("req.url ~ "+req.url);
return (synth(200, "BAN URLs containing (" + req.url + ") done."));
}
# Only handle relevant HTTP request methods
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);
}
if (req.http.Authorization) {
return (pass);
}
# We only deal with GET and HEAD by default
if (req.method != "GET" && req.method != "HEAD") {
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);
}
# Mitigate httpoxy application vulnerability, see: https://httpoxy.org/
unset req.http.Proxy;
cookie.parse(req.http.cookie);
set req.http.cache-hash = cookie.get("sw-cache-hash");
set req.http.currency = cookie.get("sw-currency");
set req.http.states = cookie.get("sw-states");
if (req.url == "/widgets/checkout/info" && !req.http.states ~ "cart-filled") {
return (synth(204, ""));
}
# Strip query strings only needed by browser javascript. Customize to used tags.
if (req.url ~ "(\?|&)(_bta_c|_bta_tid|_ga|_ke|adgid|adgroupid|adid|awcpk_campaign|camid|campaignid|chl|cof|cx|dm_i|dv|ef_id|epik|fb_action_ids|fb_action_types|fb_source|fbclid|gad_source|gclid|gclsrc|gdffi|gdfms|gdftrk|ie|kw|kwid|matomo_campaign|matomo_cid|matomo_content|matomo_group|matomo_keyword|matomo_kwd|matomo_medium|matomo_placement|matomo_source|mc_cid|mc_eid|mkwid|msclkid|mtm_campaign|mtm_cid|mtm_content|mtm_group|mtm_keyword|mtm_kwd|mtm_medium|mtm_placement|mtm_source|nk|pa|pcrid|piwik_campaign|piwik_kwd|pixelId|pk_campaign|pk_keyword|pk_kwd|pp|redirect_log_mongo_id|redirect_mongo_id|s_kwcid|sb_referer_host|siteurl|sv1|sv_campaign_id|trk_contact|trk_module|trk_msg|trk_sid|srsltid|utm_[a-z]+)=") {
# see rfc3986#section-2.3 "Unreserved Characters" for regex
set req.url = regsuball(req.url, "(_bta_c|_bta_tid|_ga|_ke|adgid|adgroupid|adid|awcpk_campaign|camid|campaignid|chl|cof|cx|dm_i|dv|ef_id|epik|fb_action_ids|fb_action_types|fb_source|fbclid|gad_source|gclid|gclsrc|gdffi|gdfms|gdftrk|ie|kw|kwid|matomo_campaign|matomo_cid|matomo_content|matomo_group|matomo_keyword|matomo_kwd|matomo_medium|matomo_placement|matomo_source|mc_cid|mc_eid|mkwid|msclkid|mtm_campaign|mtm_cid|mtm_content|mtm_group|mtm_keyword|mtm_kwd|mtm_medium|mtm_placement|mtm_source|nk|pa|pcrid|piwik_campaign|piwik_kwd|pixelId|pk_campaign|pk_keyword|pk_kwd|pp|redirect_log_mongo_id|redirect_mongo_id|s_kwcid|sb_referer_host|siteurl|sv1|sv_campaign_id|trk_contact|trk_module|trk_msg|trk_sid|srsltid|utm_[a-z]+)=[A-Za-z0-9\-\_\.\~]+&?", "");
}
set req.url = regsub(req.url, "(\?|\?&|&)$", "");
# Normalize query arguments
set req.url = std.querysort(req.url);
# Set a header announcing Surrogate Capability to the origin
set req.http.Surrogate-Capability = "shopware=ESI/1.0";
# 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;
}
return (hash);
}
sub vcl_hash {
# Consider Shopware HTTP cache cookies
if (req.http.cache-hash != "") {
hash_data("+context=" + req.http.cache-hash);
} elseif (req.http.currency != "") {
hash_data("+currency=" + req.http.currency);
}
}
sub vcl_hit {
# Consider client states for response headers
if (req.http.states) {
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_backend_fetch {
unset bereq.http.cache-hash;
unset bereq.http.currency;
unset bereq.http.states;
}
sub vcl_backend_response {
# Serve stale content for three days after object expiration
set beresp.grace = 3d;
unset beresp.http.X-Powered-By;
unset beresp.http.Server;
if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
unset beresp.http.Surrogate-Control;
set beresp.do_esi = true;
return (deliver);
}
if (bereq.url ~ "\.js$" || beresp.http.content-type ~ "text") {
set beresp.do_stream = false;
}
if (beresp.ttl > 0s && (bereq.method == "GET" || bereq.method == "HEAD")) {
unset beresp.http.Set-Cookie;
}
}
sub vcl_deliver {
## we don't want the client to cache
if (resp.http.Cache-Control !~ "private" && req.url !~ "^/(theme|media|thumbnail|bundles)/") {
set resp.http.Pragma = "no-cache";
set resp.http.Expires = "-1";
set resp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
}
# invalidation headers are only for internal use
unset resp.http.sw-invalidation-states;
unset resp.http.xkey;
unset resp.http.X-Varnish;
unset resp.http.Via;
unset resp.http.Link;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment