diff --git a/deploy/varnish/default.vcl.template b/deploy/varnish/default.vcl.template index d381169..bdbb357 100644 --- a/deploy/varnish/default.vcl.template +++ b/deploy/varnish/default.vcl.template @@ -28,19 +28,28 @@ sub vcl_recv { 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 + # Specjalna obsługa WebSocket i socket.io – BARDZO WCZEŚNIE if (req.http.Upgrade ~ "(?i)websocket" || req.url ~ "^/socket.io/") { return (pipe); } + # omijamy cache dla healthchecków / wewnętrznych nagłówków + if (req.url == "/healthcheck" || req.http.X-Internal-Check) { + set req.http.X-Pass-Reason = "internal"; + return (pass); + } + # metody inne niż GET/HEAD bez cache - if (req.method != "GET" && req.method != "HEAD") { return (pass); } + if (req.method != "GET" && req.method != "HEAD") { + set req.http.X-Pass-Reason = "method"; + return (pass); + } # Żądania z Authorization nie są buforowane - if (req.http.Authorization) { return (pass); } + if (req.http.Authorization) { + set req.http.X-Pass-Reason = "auth"; + return (pass); + } # ---- Normalizacja Accept-Encoding (kolejność: zstd > br > gzip) ---- if (req.http.Accept-Encoding) { @@ -56,7 +65,8 @@ sub vcl_recv { } # ---- STATYCZNE – agresywny cache + ignorujemy sesję ---- - if (req.url ~ "^/static/" || req.url ~ "\.(css|js|png|jpe?g|webp|svg|ico|woff2?)$") { + # Użyj double escape jak w działającej wersji! + 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); @@ -66,30 +76,19 @@ sub vcl_recv { set req.http.X-Forwarded-Proto = "https"; } - if (req.url == "/healthcheck" || req.http.X-Internal-Check) { - set req.http.X-Pass-Reason = "internal"; - return (pass); - } - - if (req.method != "GET" && req.method != "HEAD") { - set req.http.X-Pass-Reason = "method"; - return (pass); - } - - if (req.http.Authorization) { - set req.http.X-Pass-Reason = "auth"; - return (pass); - } - + # BRAK DUPLIKATÓW – koniec wyjątków, reszta do hash return (hash); } -# ===== PIPE (WebSocket passthrough) ===== +# ===== PIPE (WebSocket passthrough) – poprawione ===== sub vcl_pipe { + # Kopiuj WS headers z klienta do backendu if (req.http.Upgrade) { set bereq.http.Upgrade = req.http.Upgrade; set bereq.http.Connection = req.http.Connection; } + # WAŻNE: backend nie może reuse połączenia po WS + set bereq.http.connection = "close"; } # ===== HASH ===== @@ -125,9 +124,9 @@ sub vcl_backend_response { return (deliver); } - # Nie cache'uj statyków, jeśli status ≠ 200 + # Nie cache'uj statyków, jeśli status ≠ 200 – double escape if (bereq.url ~ "^/static/" || - bereq.url ~ "\.(css|js|png|jpe?g|webp|svg|ico|woff2?)($|\?)") { + bereq.url ~ "\\.(css|js|png|jpe?g|webp|svg|ico|woff2?)($|\\?)") { if (beresp.status != 200) { set beresp.uncacheable = true; set beresp.ttl = 0s; @@ -136,31 +135,31 @@ sub vcl_backend_response { } # Jeśli pod .js przychodzi text/html — też nie cache'uj (to zwykle redirect/login) - if (bereq.url ~ "\.js(\?.*)?$" && beresp.http.Content-Type ~ "(?i)text/html") { + if (bereq.url ~ "\\.js(\\?.*)?$" && beresp.http.Content-Type ~ "(?i)text/html") { set beresp.uncacheable = true; set beresp.ttl = 0s; return (deliver); } # Wymuś poprawny Content-Type dla .js/.css, gdy backend zwróci HTML - if (bereq.url ~ "\.js(\?.*)?$") { + if (bereq.url ~ "\\.js(\\?.*)?$") { if (!beresp.http.Content-Type || beresp.http.Content-Type ~ "(?i)text/html") { set beresp.http.Content-Type = "application/javascript; charset=utf-8"; } } - if (bereq.url ~ "\.css(\?.*)?$") { + if (bereq.url ~ "\\.css(\\?.*)?$") { if (!beresp.http.Content-Type || beresp.http.Content-Type ~ "(?i)text/html") { set beresp.http.Content-Type = "text/css; charset=utf-8"; } } # ---- STATYCZNE: zdejmij Set-Cookie i Vary: Cookie, zapewnij TTL ---- - if (bereq.url ~ "^/static/" || bereq.url ~ "\.(css|js|png|jpe?g|webp|svg|ico|woff2?)$") { + 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, "(?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:]]*$", ""); @@ -179,9 +178,9 @@ sub vcl_backend_response { # ---- 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); + 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); + 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; } @@ -196,9 +195,7 @@ sub vcl_backend_response { } # 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; wykluczamy WASM if (beresp.http.Content-Type ~ "(?i)text/|application/(javascript|json|xml)") { set beresp.do_gzip = true; } @@ -210,7 +207,6 @@ sub vcl_backend_response { } } -# (Opcjonalnie) Serwuj „stale" przy błędach backendu, jeśli jest obiekt w grace sub vcl_backend_error { return (deliver); } @@ -239,25 +235,22 @@ sub vcl_deliver { unset resp.http.Server; } -# ===== SYNTH - naprawiony składniowo ===== +# ===== SYNTH ===== sub vcl_synth { unset resp.http.X-Varnish; - # 429 Too Many Requests - HTML szablon (jedna długa linia) if (resp.status == 429) { set resp.http.Content-Type = "text/html; charset=utf-8"; synthetic({"429 - To many requests

429

To many requests!

Limit: 200 req / 10 s

"}); return (deliver); } - # 405 Not Allowed if (resp.status == 405) { set resp.http.Content-Type = "text/plain; charset=utf-8"; synthetic("Metoda PURGE dozwolona tylko z localhost"); return (deliver); } - # PURGE success if (resp.status == 200 && req.method == "PURGE") { set resp.http.Content-Type = "text/plain; charset=utf-8"; synthetic("Purged");