From 9a7660fdf7ff960dacc0b5d8dd6f303ef3650e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Wed, 24 Sep 2025 16:02:48 +0200 Subject: [PATCH] varnish throttle --- deploy/varnish/default.vcl.template | 56 ++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/deploy/varnish/default.vcl.template b/deploy/varnish/default.vcl.template index e8f0032..1dbe11b 100644 --- a/deploy/varnish/default.vcl.template +++ b/deploy/varnish/default.vcl.template @@ -1,6 +1,7 @@ vcl 4.1; import vsthrottle; +import std; backend app { .host = "app"; @@ -10,8 +11,8 @@ backend app { acl purge { "localhost"; "127.0.0.1"; } sub vcl_recv { - # RATE LIMIT: 10 żądań / 10s, po przekroczeniu blokada na 30s - if (vsthrottle.is_denied(client.identity, 10, 10s, 30s)) { + # RATE LIMIT: 100 żądań / 10s, po przekroczeniu blokada na 60s + if (vsthrottle.is_denied(client.identity, 100, 10s, 60s)) { return (synth(429, "Too Many Requests")); } @@ -36,31 +37,54 @@ sub vcl_recv { } sub vcl_backend_response { - if (bereq.url ~ "^/static/" || bereq.url ~ "\.(css|js|png|jpg|svg|ico|woff2?)$") { - set beresp.ttl = 24h; + # Treści prywatne / zakazane do cache + if (beresp.http.Cache-Control ~ "(?i)no-store|private") { + set beresp.uncacheable = true; + set beresp.ttl = 0s; + return; + } + + # Preferuj s-maxage (cache współdzielony), potem max-age + 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) { + # fallback na Expires + set beresp.ttl = std.duration(std.timestamp2s(beresp.http.Expires) - std.now(), 0s); } else { - if (beresp.http.Cache-Control ~ "no-cache|no-store|private") { - set beresp.uncacheable = true; - set beresp.ttl = 0s; - } else { - set beresp.ttl = 60s; # domyślny TTL - } + # ostateczny fallback + set beresp.ttl = 60s; + } + + # Jeśli immutable – zwiększ grace (serwuj „stale” dłużej przy problemach z backendem) + if (beresp.http.Cache-Control ~ "(?i)immutable") { + set beresp.grace = 1h; # dostosuj wg potrzeb + set beresp.keep = 24h; # dłuższe trzymanie w storage na trafienia „stale-if-error” + } + + # (opcjonalnie) statykom daj minimalne TTL, gdy backend NIE ustawił CC + if ((bereq.url ~ "^/static/" || bereq.url ~ "\.(css|js|png|jpg|svg|ico|woff2?)$") + && !(beresp.http.Cache-Control ~ "(?i)(s-maxage|max-age)")) { + set beresp.ttl = 24h; } } + sub vcl_deliver { if (obj.hits > 0) { set resp.http.X-Cache = "HIT"; - } else { - set resp.http.X-Cache = "MISS"; + # ukryhe niepotrzebny nagłówek z MISS + #} else { + # set resp.http.X-Cache = "MISS"; } # Nagłówki rate limit – MUSZĄ używać tej samej czwórki parametrów co is_denied() - set resp.http.X-RateLimit-Limit = "10"; - set resp.http.X-RateLimit-Window = "10s"; - set resp.http.X-RateLimit-Remaining = vsthrottle.remaining(client.identity, 10, 10s, 30s); - set resp.http.Retry-After = vsthrottle.blocked(client.identity, 10, 10s, 30s); + #set resp.http.X-RateLimit-Limit = "10"; + #set resp.http.X-RateLimit-Window = "10s"; + #set resp.http.X-RateLimit-Remaining = vsthrottle.remaining(client.identity, 10, 10s, 30s); + #set resp.http.Retry-After = vsthrottle.blocked(client.identity, 10, 10s, 30s); unset resp.http.Via; unset resp.http.X-Varnish;