345 lines
10 KiB
Plaintext
345 lines
10 KiB
Plaintext
vcl 4.1;
|
|
|
|
import std;
|
|
import vsthrottle;
|
|
|
|
# Cache-Status zgodny z RFC 9211. Wymaga vmod_vsthrottle.
|
|
backend default {
|
|
.host = "plik";
|
|
.port = "8080";
|
|
.max_connections = 100;
|
|
.probe = {
|
|
.url = "/";
|
|
.interval = 10s;
|
|
.timeout = 5s;
|
|
.window = 5;
|
|
.threshold = 3;
|
|
}
|
|
.connect_timeout = 5s;
|
|
.first_byte_timeout = 90s;
|
|
.between_bytes_timeout = 2s;
|
|
}
|
|
|
|
acl purge {
|
|
"127.0.0.1";
|
|
"::1";
|
|
}
|
|
|
|
sub vcl_recv {
|
|
# Wyczyść nieufne nagłówki diagnostyczne od klienta.
|
|
unset req.http.X-Cache;
|
|
unset req.http.X-Cache-Hits;
|
|
unset req.http.Cache-Status;
|
|
unset req.http.X-CS-Decision;
|
|
|
|
if (req.http.Host) {
|
|
set req.http.Host = regsub(req.http.Host, ":[0-9]+$", "");
|
|
}
|
|
unset req.http.Proxy;
|
|
set req.backend_hint = default;
|
|
set req.url = std.querysort(req.url);
|
|
set req.url = regsub(req.url, "\?$", "");
|
|
|
|
if (req.restarts == 0) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (vsthrottle.is_denied(client.identity, 100, 10s, 10s)) {
|
|
return (synth(429, "Too Many Requests"));
|
|
}
|
|
|
|
if (req.method == "PURGE") {
|
|
if (!client.ip ~ purge) {
|
|
return (synth(405, "Not allowed."));
|
|
}
|
|
return (purge);
|
|
}
|
|
|
|
if (req.method == "BAN") {
|
|
if (!client.ip ~ purge) {
|
|
return (synth(405, "Not allowed."));
|
|
}
|
|
# Lokalny BAN zachowuje pierwotną semantykę wyrażenia regularnego.
|
|
ban("req.http.host == " + req.http.host + " && req.url ~ " + req.url);
|
|
return (synth(200, "Banned"));
|
|
}
|
|
|
|
# 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);
|
|
}
|
|
|
|
if (req.http.Authorization) {
|
|
set req.http.X-CS-Decision = "bypass";
|
|
return (pass);
|
|
}
|
|
|
|
# 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 (
|
|
req.url ~ "(?i)^/(upload|api|status|progress|session|login|logout)(/|$|\?)" ||
|
|
req.url ~ "(?i)/(ajax|ahah)/"
|
|
) {
|
|
set req.http.X-CS-Decision = "bypass";
|
|
return (pass);
|
|
}
|
|
|
|
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)(\?.*)?$") {
|
|
unset req.http.Accept-Encoding;
|
|
} elseif (req.http.Accept-Encoding ~ "(?i)gzip") {
|
|
set req.http.Accept-Encoding = "gzip";
|
|
} else {
|
|
unset req.http.Accept-Encoding;
|
|
}
|
|
}
|
|
|
|
# Cookies ignorujemy wyłącznie dla zasobów statycznych.
|
|
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)(\?.*)?$") {
|
|
unset req.http.Cookie;
|
|
} else {
|
|
set req.http.X-CS-Decision = "bypass";
|
|
return (pass);
|
|
}
|
|
}
|
|
|
|
return (hash);
|
|
}
|
|
|
|
sub vcl_hash {
|
|
# Domyślny VCL dodaje req.url i Host. Dodajemy rozróżnienie HTTP/HTTPS.
|
|
if (req.http.X-Forwarded-Proto) {
|
|
hash_data(req.http.X-Forwarded-Proto);
|
|
}
|
|
}
|
|
|
|
sub vcl_hit {
|
|
# Varnish przechodzi do vcl_miss sam, kiedy TTL i grace wygasna.
|
|
# Dla swiezego obiektu lub obiektu w grace zwracamy HIT.
|
|
set req.http.X-CS-Decision = "hit";
|
|
return (deliver);
|
|
}
|
|
|
|
sub vcl_miss {
|
|
if (!req.http.X-CS-Decision) {
|
|
set req.http.X-CS-Decision = "miss";
|
|
}
|
|
return (fetch);
|
|
}
|
|
|
|
sub vcl_pass {
|
|
if (!req.http.X-CS-Decision) {
|
|
set req.http.X-CS-Decision = "bypass";
|
|
}
|
|
return (fetch);
|
|
}
|
|
|
|
sub vcl_backend_fetch {
|
|
# Metadane pomocnicze nie trafiają do aplikacji.
|
|
unset bereq.http.X-CS-Decision;
|
|
}
|
|
|
|
sub vcl_backend_response {
|
|
# Dla PASS nie zmieniaj polityki cache ani cookies aplikacji.
|
|
if (bereq.uncacheable) {
|
|
set beresp.uncacheable = true;
|
|
set beresp.ttl = 0s;
|
|
return (deliver);
|
|
}
|
|
|
|
# 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) {
|
|
set beresp.ttl = 5s;
|
|
} elseif (beresp.status == 301 || beresp.status == 302) {
|
|
set beresp.ttl = 10m;
|
|
}
|
|
|
|
# Wariant: statyki bez jawnej polityki pochodzenia dostają 7 dni.
|
|
if (beresp.status == 200 &&
|
|
bereq.url ~ "(?i)\.(jpg|jpeg|png|gif|webp|ico|svg|js|css|woff2?|ttf|eot|pdf|zip|7z|gz|bz2|mp4|mp3)(\?.*)?$" &&
|
|
!beresp.http.Cache-Control && !beresp.http.Expires) {
|
|
set beresp.ttl = 7d;
|
|
set beresp.http.Cache-Control = "public, max-age=604800, immutable";
|
|
}
|
|
|
|
# Gdy Varnish nie ma dodatniego TTL, odczytaj nagłówki cache.
|
|
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.uncacheable = true;
|
|
return (deliver);
|
|
}
|
|
|
|
# Set-Cookie usuwamy tylko przy odpowiedzi 200 z publicznym plikiem.
|
|
if (beresp.status == 200 &&
|
|
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;
|
|
}
|
|
|
|
# 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") {
|
|
unset beresp.http.Surrogate-Control;
|
|
set beresp.do_esi = true;
|
|
}
|
|
|
|
set beresp.grace = 10m;
|
|
if (beresp.ttl > 1h) {
|
|
set beresp.keep = 1h;
|
|
} else {
|
|
set beresp.keep = beresp.ttl;
|
|
}
|
|
|
|
if (beresp.http.Content-Length && std.integer(beresp.http.Content-Length, 0) > 1048576) {
|
|
set beresp.do_stream = true;
|
|
}
|
|
|
|
return (deliver);
|
|
}
|
|
|
|
sub vcl_deliver {
|
|
# 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)(\?.*)?$" &&
|
|
resp.status == 200 && !obj.uncacheable) {
|
|
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";
|
|
}
|
|
|
|
# Odpowiedz z vcl_backend_error to lokalny synthetic response.
|
|
# RFC 9211 zaleca, aby nie oznaczac jej jako MISS.
|
|
if (resp.http.X-CS-Local-Error) {
|
|
unset resp.http.Cache-Status;
|
|
} else {
|
|
# RFC 9211: status dotyczy obsługi bieżącego żądania (nie obiektu w cache).
|
|
# 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") {
|
|
if (!obj.uncacheable && obj.ttl > 0s) {
|
|
set resp.http.X-CS-Entry = "varnish; fwd=uri-miss; stored";
|
|
} else {
|
|
set resp.http.X-CS-Entry = "varnish; fwd=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-Host;
|
|
unset resp.http.Via;
|
|
unset resp.http.X-Varnish;
|
|
unset resp.http.Server;
|
|
unset resp.http.Content-Disposition;
|
|
|
|
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 {
|
|
# 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;
|
|
set resp.http.Cache-Control = "private, no-store";
|
|
|
|
if (resp.status == 429) {
|
|
set resp.http.Content-Type = "text/plain; charset=utf-8";
|
|
return (deliver);
|
|
}
|
|
# Dla PURGE/BAN i innych SYNTH zachowaj tresc built-in VCL.
|
|
}
|