Update varnish/default.vcl

This commit is contained in:
gru committed 2026-10-26 16:31:31 +01:00
1 parent 7eb606e02c
commit 92a87fdb56
1 file changed
+183 -79
+183 -79
View File
@@ -3,6 +3,7 @@ vcl 4.1;
import std; import std;
import vsthrottle; import vsthrottle;
# Cache-Status zgodny z RFC 9211. Wymaga vmod_vsthrottle.
backend default { backend default {
.host = "plik"; .host = "plik";
.port = "8080"; .port = "8080";
@@ -26,11 +27,16 @@ acl purge {
} }
sub vcl_recv { sub vcl_recv {
# Wyczyść nieufne nagłówki diagnostyczne od klienta.
unset req.http.X-Cache; unset req.http.X-Cache;
unset req.http.X-Cache-Hits; unset req.http.X-Cache-Hits;
unset req.http.Cache-Status;
unset req.http.X-CS-Decision;
set req.http.Host = regsub(req.http.Host, ":[0-9]+", ""); if (req.http.Host) {
unset req.http.proxy; set req.http.Host = regsub(req.http.Host, ":[0-9]+$", "");
}
unset req.http.Proxy;
set req.backend_hint = default; set req.backend_hint = default;
set req.url = std.querysort(req.url); set req.url = std.querysort(req.url);
set req.url = regsub(req.url, "\?$", ""); set req.url = regsub(req.url, "\?$", "");
@@ -58,43 +64,54 @@ sub vcl_recv {
if (!client.ip ~ purge) { if (!client.ip ~ purge) {
return (synth(405, "Not allowed.")); return (synth(405, "Not allowed."));
} }
# Lokalny BAN zachowuje pierwotną semantykę wyrażenia regularnego.
ban("req.http.host == " + req.http.host + " && req.url ~ " + req.url); ban("req.http.host == " + req.http.host + " && req.url ~ " + req.url);
return (synth(200, "Banned")); return (synth(200, "Banned"));
} }
if (req.method != "GET" && req.method != "HEAD" && req.method != "OPTIONS") { # OPTIONS nie jest cache'owalne; inne metody niż GET/HEAD też pomijamy.
if (req.method != "GET" && req.method != "HEAD") {
set req.http.X-CS-Decision = "method";
return (pass); return (pass);
} }
if (req.http.Authorization) { if (req.http.Authorization) {
set req.http.X-CS-Decision = "bypass";
return (pass); return (pass);
} }
# upload / status / api / ajax poza cache # Klient wymaga ponownej walidacji: bezpiecznie kierujemy do backendu.
if (req.http.Cache-Control ~ "(?i)(^|,)[[:space:]]*(no-cache|max-age=0)([[:space:]]*(,|$))" ||
req.http.Pragma ~ "(?i)no-cache") {
set req.http.X-CS-Decision = "request";
return (pass);
}
# upload / status / api / ajax poza cache.
if ( if (
req.url ~ "(?i)^/(upload|api|status|progress|session|login|logout)(/|$)" || req.url ~ "(?i)^/(upload|api|status|progress|session|login|logout)(/|$|\?)" ||
req.url ~ "(?i)/(ajax|ahah)/" req.url ~ "(?i)/(ajax|ahah)/"
) { ) {
set req.http.X-CS-Decision = "bypass";
return (pass); return (pass);
} }
if (req.http.Accept-Encoding) { if (req.http.Accept-Encoding) {
if (req.url ~ "(?i)\.(jpg|jpeg|png|gif|webp|ico|svg|woff2?|ttf|eot|mp4|mp3|pdf|zip|7z|gz|bz2)$") { if (req.url ~ "(?i)\.(jpg|jpeg|png|gif|webp|ico|svg|woff2?|ttf|eot|mp4|mp3|pdf|zip|7z|gz|bz2)(\?.*)?$") {
unset req.http.Accept-Encoding; unset req.http.Accept-Encoding;
} elseif (req.http.Accept-Encoding ~ "gzip") { } elseif (req.http.Accept-Encoding ~ "(?i)gzip") {
set req.http.Accept-Encoding = "gzip"; set req.http.Accept-Encoding = "gzip";
} else { } else {
unset req.http.Accept-Encoding; unset req.http.Accept-Encoding;
} }
} }
# jeśli są cookies, to: # Cookies ignorujemy wyłącznie dla zasobów statycznych.
# - dla plików statycznych ignorujemy
# - dla reszty omijamy cache
if (req.http.Cookie) { if (req.http.Cookie) {
if (req.url ~ "(?i)\.(jpg|jpeg|png|gif|webp|ico|svg|js|css|woff2?|ttf|eot|pdf|zip|7z|gz|bz2|mp4|mp3)$") { if (req.url ~ "(?i)\.(jpg|jpeg|png|gif|webp|ico|svg|js|css|woff2?|ttf|eot|pdf|zip|7z|gz|bz2|mp4|mp3)(\?.*)?$") {
unset req.http.Cookie; unset req.http.Cookie;
} else { } else {
set req.http.X-CS-Decision = "bypass";
return (pass); return (pass);
} }
} }
@@ -103,99 +120,134 @@ sub vcl_recv {
} }
sub vcl_hash { sub vcl_hash {
# Domyślny VCL dodaje req.url i Host. Dodajemy rozróżnienie HTTP/HTTPS.
if (req.http.X-Forwarded-Proto) { if (req.http.X-Forwarded-Proto) {
hash_data(req.http.X-Forwarded-Proto); hash_data(req.http.X-Forwarded-Proto);
} }
} }
sub vcl_hit { sub vcl_hit {
set req.http.X-Cache = "HIT"; if (obj.ttl >= 0s) {
if (obj.ttl <= 0s && obj.grace > 0s) { set req.http.X-CS-Decision = "hit";
set req.http.X-Cache = "HIT-GRACE"; return (deliver);
} }
return (deliver); if (obj.ttl + obj.grace > 0s) {
set req.http.X-CS-Decision = "hit";
return (deliver);
}
# Odswiezenie po wyczerpaniu grace to fwd=stale, nie uri-miss.
set req.http.X-CS-Decision = "stale";
return (miss);
} }
sub vcl_miss { sub vcl_miss {
set req.http.X-Cache = "MISS"; if (!req.http.X-CS-Decision) {
set req.http.X-CS-Decision = "miss";
}
return (fetch); return (fetch);
} }
sub vcl_pass { sub vcl_pass {
set req.http.X-Cache = "PASS"; if (!req.http.X-CS-Decision) {
set req.http.X-CS-Decision = "bypass";
}
return (fetch); return (fetch);
} }
sub vcl_backend_fetch {
# Metadane pomocnicze nie trafiają do aplikacji.
unset bereq.http.X-CS-Decision;
}
sub vcl_backend_response { sub vcl_backend_response {
# nie cache'uj błędów 5xx # Dla PASS nie zmieniaj polityki cache ani cookies aplikacji.
if (beresp.status == 500 || beresp.status == 503) { if (bereq.uncacheable) {
set beresp.uncacheable = true; set beresp.uncacheable = true;
set beresp.ttl = 0s; set beresp.ttl = 0s;
return (deliver); return (deliver);
} }
# krótkie cache dla 404, umiarkowane dla redirectów # Odpowiedzi 5xx i tresci jawnie prywatne nie moga trafic do cache.
if (beresp.status >= 500 && beresp.status <= 599) {
set beresp.uncacheable = true;
set beresp.ttl = 0s;
return (deliver);
}
if (beresp.http.Cache-Control ~ "(?i)(no-store|private|no-cache)" ||
beresp.http.Surrogate-Control ~ "(?i)no-store" ||
beresp.http.Vary ~ "^[[:space:]]*\*[[:space:]]*$") {
set beresp.uncacheable = true;
set beresp.ttl = 0s;
return (deliver);
}
# Odpowiedź personalizowana z Set-Cookie nie powinna być współdzielona.
if (bereq.url !~ "(?i)\.(jpg|jpeg|png|gif|webp|ico|svg|js|css|woff2?|ttf|eot|pdf|zip|7z|gz|bz2|mp4|mp3)(\?.*)?$" &&
beresp.http.Set-Cookie) {
set beresp.uncacheable = true;
set beresp.ttl = 0s;
return (deliver);
}
# Krótki cache dla 404, umiarkowany dla przekierowań.
if (beresp.status == 404) { if (beresp.status == 404) {
set beresp.ttl = 5s; set beresp.ttl = 5s;
} elseif (beresp.status == 301 || beresp.status == 302) { } elseif (beresp.status == 301 || beresp.status == 302) {
set beresp.ttl = 10m; set beresp.ttl = 10m;
} }
if (bereq.url ~ "(?i)\.(jpg|jpeg|png|gif|webp|ico|svg|mp4|mp3|pdf|zip|7z|gz|bz2)$") { # Wariant: statyki bez jawnej polityki pochodzenia dostają 7 dni.
set beresp.do_gzip = false; if (beresp.status == 200 &&
} else { bereq.url ~ "(?i)\.(jpg|jpeg|png|gif|webp|ico|svg|js|css|woff2?|ttf|eot|pdf|zip|7z|gz|bz2|mp4|mp3)(\?.*)?$" &&
set beresp.do_gzip = true; !beresp.http.Cache-Control && !beresp.http.Expires) {
set beresp.ttl = 7d;
set beresp.http.Cache-Control = "public, max-age=604800, immutable";
} }
if (beresp.http.Cache-Control ~ "(?i)no-store|private") { # Gdy Varnish nie ma dodatniego TTL, odczytaj nagłówki cache.
set beresp.uncacheable = true; if (beresp.ttl <= 0s) {
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
);
} elseif (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
);
}
}
if (beresp.ttl <= 0s) {
set beresp.ttl = 0s; set beresp.ttl = 0s;
set beresp.uncacheable = true;
return (deliver); return (deliver);
} }
if (beresp.ttl <= 0s) { # Set-Cookie usuwamy tylko przy odpowiedzi 200 z publicznym plikiem.
if (beresp.http.Cache-Control ~ "(?i)s-maxage=\d+") { if (beresp.status == 200 &&
set beresp.ttl = std.duration( bereq.url ~ "(?i)\.(jpg|jpeg|png|gif|webp|ico|svg|js|css|woff2?|ttf|eot|pdf|zip|7z|gz|bz2|mp4|mp3)(\?.*)?$") {
regsub(beresp.http.Cache-Control, ".*(?i)s-maxage=(\d+).*", "\1") + "s",
0s
);
} elseif (beresp.http.Cache-Control ~ "(?i)max-age=\d+") {
set beresp.ttl = std.duration(
regsub(beresp.http.Cache-Control, ".*(?i)max-age=(\d+).*", "\1") + "s",
0s
);
}
}
if (beresp.ttl <= 0s) {
if (bereq.url ~ "(?i)\.(jpg|jpeg|png|gif|webp|ico|svg|js|css|woff2?|ttf|eot|pdf|zip|7z|gz|bz2|mp4|mp3)$") {
set beresp.ttl = 7d;
} else {
set beresp.ttl = 0s;
set beresp.uncacheable = true;
return (deliver);
}
}
if (bereq.url ~ "(?i)\.(jpg|jpeg|png|gif|webp|ico|svg|js|css|woff2?|ttf|eot|pdf|zip|7z|gz|bz2|mp4|mp3)$") {
unset beresp.http.Set-Cookie; unset beresp.http.Set-Cookie;
} }
# Kompresuj tylko nieskompresowane typy tekstowe.
if (!beresp.http.Content-Encoding &&
beresp.http.Content-Type ~ "(?i)^(text/|application/(javascript|json|xml))") {
set beresp.do_gzip = true;
} else {
set beresp.do_gzip = false;
}
if (beresp.http.Surrogate-Control ~ "ESI/1.0") { if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
unset beresp.http.Surrogate-Control; unset beresp.http.Surrogate-Control;
set beresp.do_esi = true; set beresp.do_esi = true;
} }
if (beresp.ttl > 0s) { set beresp.grace = 10m;
set beresp.grace = 10m; if (beresp.ttl > 1h) {
if (beresp.ttl > 1h) { set beresp.keep = 1h;
set beresp.keep = 1h;
} else {
set beresp.keep = beresp.ttl;
}
} else { } else {
set beresp.grace = 0s; set beresp.keep = beresp.ttl;
set beresp.keep = 0s;
} }
if (beresp.http.Content-Length && std.integer(beresp.http.Content-Length, 0) > 1048576) { if (beresp.http.Content-Length && std.integer(beresp.http.Content-Length, 0) > 1048576) {
@@ -206,29 +258,70 @@ sub vcl_backend_response {
} }
sub vcl_deliver { sub vcl_deliver {
# Cache-Control tylko dla realnych plików # Polityka przeglądarki: nie nadpisuj private/no-store backendu na public.
if (req.url ~ "(?i)\.(jpg|jpeg|png|gif|webp|ico|svg|js|css|woff2?|ttf|eot|pdf|zip|7z|gz|bz2|mp4|mp3)$") { if (req.url ~ "(?i)\.(jpg|jpeg|png|gif|webp|ico|svg|js|css|woff2?|ttf|eot|pdf|zip|7z|gz|bz2|mp4|mp3)(\?.*)?$" &&
set resp.http.Cache-Control = "public, max-age=604800, immutable"; resp.status == 200 && !obj.uncacheable) {
} else { if (!resp.http.Cache-Control) {
set resp.http.Cache-Control = "public, max-age=604800, immutable";
}
} elseif (!resp.http.Cache-Control ||
resp.http.Cache-Control !~ "(?i)(no-store|private)") {
set resp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0"; set resp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
} }
unset resp.http.Expires; # Odpowiedz z vcl_backend_error to lokalny synthetic response.
unset resp.http.Pragma; # RFC 9211 zaleca, aby nie oznaczac jej jako MISS.
if (resp.http.X-CS-Local-Error) {
if (resp.status >= 500) { unset resp.http.Cache-Status;
set resp.http.X-Cache = "ERROR";
} elseif (obj.uncacheable) {
set resp.http.X-Cache = "PASS";
unset resp.http.Age;
} elseif (obj.hits > 0) {
set resp.http.X-Cache = "HIT";
set resp.http.X-Cache-Hits = obj.hits;
} else { } else {
set resp.http.X-Cache = "MISS"; # RFC 9211: status dotyczy obsługi bieżącego żądania (nie obiektu w cache).
unset resp.http.Age; # Dodajemy własny wpis NA KOŃCU, zachowując wpisy wcześniejszych cache.
if (req.http.X-CS-Decision == "hit") {
# obj.ttl ma format np. 123.456; RFC wymaga całkowitych sekund.
# Wariant działa również ze starszym std (Varnish 6.0/6.1).
set resp.http.X-CS-TTL = obj.ttl;
set resp.http.X-CS-TTL = regsub(resp.http.X-CS-TTL, "[.][0-9]+$", "");
set resp.http.X-CS-Entry = "varnish; hit; ttl=" + resp.http.X-CS-TTL;
} elseif (req.http.X-CS-Decision == "method") {
set resp.http.X-CS-Entry = "varnish; fwd=method";
} elseif (req.http.X-CS-Decision == "request") {
set resp.http.X-CS-Entry = "varnish; fwd=request";
} elseif (req.http.X-CS-Decision == "bypass") {
set resp.http.X-CS-Entry = "varnish; fwd=bypass";
} elseif (req.http.X-CS-Decision == "miss" || req.http.X-CS-Decision == "stale") {
if (!obj.uncacheable && obj.ttl > 0s) {
set resp.http.X-CS-Entry = "varnish; fwd=" +
regsub(req.http.X-CS-Decision, "^miss$", "uri-miss") + "; stored";
} else {
set resp.http.X-CS-Entry = "varnish; fwd=" +
regsub(req.http.X-CS-Decision, "^miss$", "uri-miss") + "; stored=?0";
}
} else {
# Zabezpieczenie na nietypowe ścieżki VCL.
if (obj.uncacheable) {
set resp.http.X-CS-Entry = "varnish; fwd=bypass";
} elseif (obj.hits > 0) {
set resp.http.X-CS-TTL = obj.ttl;
set resp.http.X-CS-TTL = regsub(resp.http.X-CS-TTL, "[.][0-9]+$", "");
set resp.http.X-CS-Entry = "varnish; hit; ttl=" + resp.http.X-CS-TTL;
} else {
set resp.http.X-CS-Entry = "varnish; fwd=uri-miss";
}
}
if (resp.http.Cache-Status) {
set resp.http.Cache-Status = resp.http.Cache-Status + ", " + resp.http.X-CS-Entry;
} else {
set resp.http.Cache-Status = resp.http.X-CS-Entry;
}
} }
unset resp.http.X-CS-Entry;
unset resp.http.X-CS-TTL;
unset resp.http.X-CS-Local-Error;
unset resp.http.X-Cache;
unset resp.http.X-Cache-Hits;
unset resp.http.X-Url; unset resp.http.X-Url;
unset resp.http.X-Host; unset resp.http.X-Host;
unset resp.http.Via; unset resp.http.Via;
@@ -239,12 +332,23 @@ sub vcl_deliver {
set resp.http.X-Frame-Options = "SAMEORIGIN"; set resp.http.X-Frame-Options = "SAMEORIGIN";
} }
sub vcl_backend_error {
# Zachowaj standardowa tresc bledu z built-in VCL, bez cache'owania 503.
set beresp.ttl = 0s;
set beresp.http.X-CS-Local-Error = "1";
}
sub vcl_synth { sub vcl_synth {
set resp.http.X-Cache = "SYNTH"; # Odpowiedzi syntetyczne (np. 429) nie raportują stanu cache.
unset resp.http.Cache-Status;
unset resp.http.X-Cache;
unset resp.http.X-Cache-Hits;
unset resp.http.X-Varnish; unset resp.http.X-Varnish;
set resp.http.Cache-Control = "private, no-store";
if (resp.status == 429) { if (resp.status == 429) {
set resp.http.Content-Type = "text/plain; charset=utf-8"; set resp.http.Content-Type = "text/plain; charset=utf-8";
return (deliver); return (deliver);
} }
} # Dla PURGE/BAN i innych SYNTH zachowaj tresc built-in VCL.
}