Skip to content
79 changes: 47 additions & 32 deletions drupal/templates/varnish-configmap-vcl.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ data:
set beresp.http.x-ttl = "10m";
}

if (bereq.url ~ "^[^?]*\.(jpg|jpeg|gif|png|svg|ico|webp|css|js|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|flv|swf|html|htm|otf)(\?.*)?$") {
if (bereq.url ~ "^[^?]*\.(?:jpg|jpeg|gif|png|svg|ico|webp|css|js|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|flv|swf|html|htm|otf)(?:\?.*)?$") {
# Strip any cookies before static files are inserted into cache.
unset beresp.http.set-cookie;
if(beresp.status == 200){
Expand All @@ -199,7 +199,7 @@ data:
# Large static files are delivered directly to the end-user without
# waiting for Varnish to fully read the file first.
# Varnish 4 fully supports Streaming, so use streaming here to avoid locking.
if (bereq.url ~ "^[^?]*\.(mp[34]|rar|tar|tgz|gz|pdf|wav|zip|bz2|xz|7z|avi|mov|ogm|mpe?g|mk[av]|webm)(\?.*)?$") {
if (bereq.url ~ "^[^?]*\.(?:mp[34]|rar|tar|tgz|gz|pdf|wav|zip|bz2|xz|7z|avi|mov|ogm|mpe?g|mk[av]|webm)(?:\?.*)?$") {
unset beresp.http.set-cookie;
set beresp.do_stream = true; # Check memory usage it'll grow in fetch_chunksize blocks (128k by default) if the backend doesn't send a Content-Length header, so only enable it for big objects
set beresp.do_gzip = false; # Don't try to compress it for storage
Expand Down Expand Up @@ -304,8 +304,8 @@ data:
return(pipe);
}

# Only allow BAN requests from IP addresses in the 'purge' ACL.
if (req.method == "BAN" || req.method == "URIBAN") {
# Only allow BAN requests from IP addressees in the 'internal' ACL.
if (req.method == "BAN") {
# Admin port is only exposed to internal network
if (!client.ip ~ purge) {
return (synth(403, "Not allowed."));
Expand All @@ -319,11 +319,30 @@ data:
elseif (req.http.Cache-Tags) {
ban("obj.http.Cache-Tags ~ " + req.http.Cache-Tags);
}
elseif (req.method == "URIBAN") {
ban("req.http.host == " + req.http.host + " && req.url == " + req.url);
else {
# If there are no cache tags headers in a BAN request,
# it is a bad request, so indicate that to the client.
return (synth(400, "Cache tags headers not present."));
}
# Throw a synthetic page so the request won't go to the backend.
return (synth(200, "Ban added."));
}

# Only allow URIBAN requests from IP addressees in the 'internal' ACL.
if (req.method == "URIBAN") {
# Admin port is only exposed to internal network
if (!client.ip ~ purge) {
return (synth(403, "Not allowed."));
Copy link
Copy Markdown
Contributor

@ragnarkurmwunder ragnarkurmwunder Aug 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider "IP not allowed".
Reasoning: suppose someone gets the error, and has no clue of this code here, it could be a guesswork why the error.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK, the message set here is only logged in varnishlog, not transmitted via HTTP.

}

# If x-url-invalidate-pattern header is present,
# use it to match URLs in stored objects. (ban by regex pattern)
if (req.http.x-url-invalidate-pattern) {
ban("obj.http.x-url ~ " + req.http.x-url-invalidate-pattern);
Copy link

Copilot AI Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ban statements reference obj.http.x-url but there's no evidence that this header is set during cache storage. The original code used req.url for URIBAN matching. If x-url is not properly set on cached objects, these ban statements will not match any cached entries.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

obj.http.x-url is stored in the cached objects here

}
# Without pattern, ban by matching host and URL exactly.
else {
return (synth(403, "Cache tags headers not present."));
ban("obj.http.host == " + req.http.host + " && obj.http.x-url == " + req.url);
Comment thread
MarttiR marked this conversation as resolved.
}
# Throw a synthetic page so the request won't go to the backend.
return (synth(200, "Ban added."));
Expand Down Expand Up @@ -406,7 +425,7 @@ data:

{{- if .Values.mailpit.enabled }}
// No varnish for mailpit
if (req.url ~ "^/mailpit(/|$)") {
if (req.url ~ "^/mailpit(?:/|$)") {
return (pass);
}
{{- end }}
Expand All @@ -420,13 +439,28 @@ data:
return (pass);
}

if (req.url ~ "\.(png|gif|jpg|tif|tiff|ico|webp|swf|css|js|pdf|doc|xls|ppt|zip)(\?.*)?$") {
// Forcing a lookup with static file requests
if (req.http.Accept-Encoding) {
if (req.url ~ "\.(?:bz2|eot|gif|gz|ico|jpg|mp3|ogg|pdf|png|svg|swf|tbz|tgz|tif|tiff|ttf|webp|woff|zip)(\?.*)?$") {
# 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.url ~ "\.(?:bmp|bz2|css|doc|eot|gif|ico|jpg|js|pdf|png|ppt|svg|swf|tif|tiff|ttf|webp|woff|xls|zip)$") {
# Static file request do not vary on cookies
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I like this.

unset req.http.Cookie;
return (hash);
}

# Do not allow public access to cron.php , update.php or install.php.
if (req.url ~ "^(|/core)/(cron|install|update)\.php$" && !client.ip ~ internal) {
if (req.url ~ "^(?:/core)?/(?:cron|install|update)\.php$" && !client.ip ~ internal) {
# Have Varnish throw the error directly.
return (synth( 404, "Page not found."));
}
Expand All @@ -445,12 +479,7 @@ data:
}

if (req.http.Cookie) {
if (req.url ~ "\.(png|gif|jpg|svg|tif|tiff|ico|webp|swf|css|js|pdf|doc|xls|ppt|zip|woff|eot|ttf|bmp|bz2)$") {
# Static file request do not vary on cookies
unset req.http.Cookie;
return (hash);
}
elseif (req.http.Cookie ~ "(SESS[a-z0-9]+|SSESS[a-z0-9]+)") {
if (req.http.Cookie ~ "S?SESS[a-z0-9]+=") {
Copy link

Copilot AI Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex pattern S?SESS[a-z0-9]+= is incorrect. The S? makes the 'S' optional, which would match both 'SESS' and 'ESS' patterns. Based on the original code that checked for (SESS[a-z0-9]+|SSESS[a-z0-9]+), this should be (S?SESS[a-z0-9]+=) or (?:S?SESS[a-z0-9]+=) to properly match both SESS and SSESS cookies.

Suggested change
if (req.http.Cookie ~ "S?SESS[a-z0-9]+=") {
if (req.http.Cookie ~ "(S?SESS[a-z0-9]+=)") {

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not true, the pattern is correct. This can be verified using regexr.com:

Screenshot 2025-08-26 at 12 15 29

# Authenticated users should not be cached
return (pass);
}
Expand All @@ -465,20 +494,6 @@ data:
}
}

if (req.http.Accept-Encoding) {
if (req.url ~ "\.(jpg|png|gif|svg|tif|tiff|ico|webp|gz|tgz|bz2|tbz|mp3|ogg|swf|zip|pdf|woff|eot|ttf)(\?.*)?$") {
# 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;
}
}

// Keep multiple cache objects to a minimum
# -> NOT. We dont Vary cache per these headers.
#unset req.http.Accept-Language;
Expand All @@ -487,7 +502,7 @@ data:
# Large static files are delivered directly to the end-user without
# waiting for Varnish to fully read the file first.
# Varnish 4 fully supports Streaming, so set do_stream in vcl_backend_response()
if (req.url ~ "^[^?]*\.(mp[34]|rar|tar|tgz|gz|wav|zip|bz2|xz|7z|avi|mov|ogm|mpe?g|mk[av]|webm)(\?.*)?$") {
if (req.url ~ "^[^?]*\.(?:mp[34]|rar|tar|tgz|gz|wav|zip|bz2|xz|7z|avi|mov|ogm|mpe?g|mk[av]|webm)(?:\?.*)?$") {
unset req.http.Cookie;
return (hash);
}
Expand Down
64 changes: 41 additions & 23 deletions frontend/templates/varnish-configmap-vcl.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,45 @@ data:
return (synth(400));
}

# Only allow BAN requests from IP addressees in the 'internal' ACL.
if (req.method == "BAN") {
# Admin port is only exposed to internal network
if (!client.ip ~ internal) {
return (synth(403, "Not allowed."));
}

# Logic for the ban, using the cache tags headers.
if (req.http.Cache-Tags) {
ban("obj.http.Cache-Tags ~ " + req.http.Cache-Tags);
}
else {
# If there are no cache tags headers in a BAN request,
# it is a bad request, so indicate that to the client.
return (synth(400, "Cache tags headers not present."));
}
# Throw a synthetic page so the request won't go to the backend.
return (synth(200, "Ban added."));
}

# Only allow URIBAN requests from IP addressees in the 'internal' ACL.
if (req.method == "URIBAN") {
# Admin port is only exposed to internal network
if (!client.ip ~ internal) {
return (synth(403, "Not allowed."));
}

# If x-url-invalidate-pattern header is present,
# use it to match URLs in stored objects. (ban by regex pattern)
if (req.http.x-url-invalidate-pattern) {
ban("obj.http.x-url ~ " + req.http.x-url-invalidate-pattern);
Comment thread
MarttiR marked this conversation as resolved.
}
else {
ban("obj.http.host == " + req.http.host + " && obj.http.x-url == " + req.url);
Comment thread
MarttiR marked this conversation as resolved.
}
# Throw a synthetic page so the request won't go to the backend.
return (synth(200, "Ban added."));
}

# Match upstream allowlist to supply headers containing real ip
if (client.ip ~ upstream_proxy && req.http.X-Envoy-External-Address) {
set req.http.X-Forwarded-For = req.http.X-Envoy-External-Address;
Expand Down Expand Up @@ -100,27 +139,6 @@ data:
return (pass);
}

# Only allow BAN requests from IP addresses in the 'purge' ACL.
if (req.method == "BAN" || req.method == "URIBAN") {
# Admin port is only exposed to internal network
if (!client.ip ~ internal) {
return (synth(403, "Not allowed."));
}

# Logic for the ban, using the cache tags headers.
if (req.http.Cache-Tags) {
ban("obj.http.Cache-Tags ~ " + req.http.Cache-Tags);
}
elseif (req.method == "URIBAN") {
ban("req.http.host == " + req.http.host + " && req.url == " + req.url);
}
else {
return (synth(403, "Cache tags headers not present."));
}
# Throw a synthetic page so the request won't go to the backend.
return (synth(200, "Ban added."));
}

# Implementing websocket support (https://www.varnish-cache.org/docs/4.0/users-guide/vcl-example-websockets.html)
if (req.http.Upgrade ~ "(?i)websocket") {
return (pipe);
Expand All @@ -140,13 +158,13 @@ data:

{{- if .Values.mailpit.enabled }}
// No varnish for mailpit
if (req.url ~ "^/mailpit(/|$)") {
if (req.url ~ "^/mailpit(?:/|$)") {
return (pass);
}
{{- end }}

if (req.http.Accept-Encoding) {
if (req.url ~ "\.(jpg|png|gif|svg|tif|tiff|ico|webp|gz|tgz|bz2|tbz|mp3|ogg|swf|zip|pdf|woff|eot|ttf)(\?.*)?$") {
if (req.url ~ "\.(?:jpg|png|gif|svg|tif|tiff|ico|webp|gz|tgz|bz2|tbz|mp3|ogg|swf|zip|pdf|woff|eot|ttf)(?:\?.*)?$") {
# No point in compressing these
unset req.http.Accept-Encoding;
} elsif (req.http.Accept-Encoding ~ "gzip") {
Expand Down
Loading