Files
zbiorki_app/deploy/varnish/default.vcl.template
Mateusz Gruszczyński 37061442d2 varnish config
2025-09-25 09:22:16 +02:00

191 lines
5.9 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

vcl 4.1;
import vsthrottle;
import std;
# ===== Backend =====
backend app {
.host = "app";
.port = "${APP_PORT}";
}
# ===== ACL =====
acl purge {
"127.0.0.1";
"::1";
}
# ===== RECV =====
sub vcl_recv {
# RATE LIMIT: 200 żądań / 10s, blokada 60s
if (vsthrottle.is_denied(client.identity, 200, 10s, 60s)) {
return (synth(429, "Too Many Requests"));
}
# PURGE tylko lokalnie
if (req.method == "PURGE") {
if (!client.ip ~ purge) { return (synth(405, "Not allowed")); }
return (purge);
}
# omijamy cache dla healthchecków / wewnętrznych nagłówków
if (req.url == "/healthcheck" || req.http.X-Internal-Check) { return (pass); }
# Specjalna obsługa WebSocket i socket.io
if (req.http.Upgrade ~ "(?i)websocket" || req.url ~ "^/socket.io/") {
return (pipe);
}
# metody inne niż GET/HEAD bez cache
if (req.method != "GET" && req.method != "HEAD") { return (pass); }
# ---- Normalizacja Accept-Encoding (kolejność preferencji: zstd > br > gzip) ----
if (req.http.Accept-Encoding) {
if (req.http.Accept-Encoding ~ "zstd") {
set req.http.Accept-Encoding = "zstd";
} else if (req.http.Accept-Encoding ~ "br") {
set req.http.Accept-Encoding = "br";
} else if (req.http.Accept-Encoding ~ "gzip") {
set req.http.Accept-Encoding = "gzip";
} else {
set req.http.Accept-Encoding = "identity";
}
}
# ---- (Opcjonalnie) Normalizacja Accept dla obrazów generowanych wariantowo ----
# Jeśli backend renderuje różne formaty obrazów (webp/jpg) pod tym samym URL (bez rozszerzenia),
# można włączyć „dwustanowy” sygnał do hasha:
#
# if (req.url ~ "\.(png|jpe?g|gif|bmp)$") {
# if (req.http.Accept ~ "image/webp") {
# set req.http.X-Accept-Image = "modern"; # webp
# } else {
# set req.http.X-Accept-Image = "legacy"; # jpg/png
# }
# }
# ---- STATYCZNE agresywny cache + ignorujemy sesję ----
if (req.url ~ "^/static/" || req.url ~ "\.(css|js|png|jpe?g|webp|svg|ico|woff2?)$") {
unset req.http.Cookie;
unset req.http.Authorization;
return (hash);
}
return (hash);
}
# ===== PIPE (WebSocket passthrough) =====
sub vcl_pipe {
if (req.http.Upgrade) {
set bereq.http.Upgrade = req.http.Upgrade;
set bereq.http.Connection = req.http.Connection;
}
}
# ===== HASH =====
sub vcl_hash {
hash_data(req.url);
if (req.http.host) { hash_data(req.http.host); } else { hash_data(server.ip); }
# Cookie: zostają dla dynamicznych (dla statyków wyczyszczone wcześniej)
if (req.http.Cookie) { hash_data(req.http.Cookie); }
# Accept-Encoding: już znormalizowany do zstd/br/gzip/identity
if (req.http.Accept-Encoding) { hash_data(req.http.Accept-Encoding); }
# (Opcjonalnie) sygnał obrazów z negocjacją po Accept
if (req.http.X-Accept-Image) { hash_data(req.http.X-Accept-Image); }
}
# ===== BACKEND_RESPONSE =====
sub vcl_backend_response {
# Zakaz cache respektujemy
if (beresp.http.Cache-Control ~ "(?i)no-store|private") {
set beresp.uncacheable = true;
set beresp.ttl = 0s;
return (deliver);
}
# ---- STATYCZNE: zdejmij Set-Cookie i Vary: Cookie, zapewnij TTL ----
if (bereq.url ~ "^/static/" || bereq.url ~ "\.(css|js|png|jpe?g|webp|svg|ico|woff2?)$") {
unset beresp.http.Set-Cookie;
# Jeśli backend dodał Vary: Cookie, usuńmy ten element (nie wpływa na statyki)
if (beresp.http.Vary) {
set beresp.http.Vary = regsuball(beresp.http.Vary, "(?i)(^|,)[[:space:]]*Cookie[[:space:]]*(,|$)", "\1");
set beresp.http.Vary = regsuball(beresp.http.Vary, ",[[:space:]]*,", ",");
set beresp.http.Vary = regsub(beresp.http.Vary, "^[[:space:]]*,[[:space:]]*", "");
set beresp.http.Vary = regsub(beresp.http.Vary, "[[:space:]]*,[[:space:]]*$", "");
if (beresp.http.Vary ~ "^[[:space:]]*$") { unset beresp.http.Vary; }
}
# Jeśli brak kontroli czasu życia ustawiamy twarde wartości
if (!(beresp.http.Cache-Control ~ "(?i)(s-maxage|max-age)")) {
set beresp.ttl = 24h;
set beresp.http.Cache-Control = "public, max-age=86400, immutable";
}
set beresp.grace = 1h;
set beresp.keep = 24h;
}
# ---- Ogólne TTL z nagłówków ----
if (beresp.http.Cache-Control ~ "(?i)s-maxage=([0-9]+)") {
set beresp.ttl = std.duration(regsub(beresp.http.Cache-Control, "(?i).*s-maxage=([0-9]+).*", "\1") + "s", 0s);
} else if (beresp.http.Cache-Control ~ "(?i)max-age=([0-9]+)") {
set beresp.ttl = std.duration(regsub(beresp.http.Cache-Control, "(?i).*max-age=([0-9]+).*", "\1") + "s", 0s);
} else if (beresp.http.Expires) {
set beresp.ttl = std.time(beresp.http.Expires, now) - now;
if (beresp.ttl < 0s) { set beresp.ttl = 0s; }
} else {
if (beresp.ttl <= 0s) { set beresp.ttl = 60s; }
}
# Immutable => dłuższe grace/keep
if (beresp.http.Cache-Control ~ "(?i)immutable") {
set beresp.grace = 1h;
set beresp.keep = 24h;
}
# Kompresja po stronie Varnisha wyłącznie dla klientów akceptujących gzip
# i tylko jeśli backend nie dostarczył już Content-Encoding.
if (!beresp.http.Content-Encoding && bereq.http.Accept-Encoding ~ "gzip") {
# Kompresujemy tylko „tekstowe” typy
if (beresp.http.Content-Type ~ "(?i)text/|application/(javascript|json|xml|wasm)") {
set beresp.do_gzip = true;
}
}
# Duże odpowiedzi streamujemy
if (beresp.http.Content-Length && std.integer(beresp.http.Content-Length, 0) > 1048576) {
set beresp.do_stream = true;
}
}
# ===== DELIVER =====
sub vcl_deliver {
if (obj.uncacheable) {
set resp.http.X-Cache = "PASS";
unset resp.http.Age;
} else if (obj.hits > 0) {
set resp.http.X-Cache = "HIT";
} else {
set resp.http.X-Cache = "MISS";
unset resp.http.Age;
}
unset resp.http.Via;
unset resp.http.X-Varnish;
unset resp.http.Server;
}
sub vcl_synth {
set resp.http.X-Cache = "SYNTH";
}
# ===== PURGE HANDLER =====
sub vcl_purge {
return (synth(200, "Purged"));
}