vcl 4.1; import vsthrottle; backend app { .host = "app"; .port = "8080"; } acl purge { "localhost"; "127.0.0.1"; } sub vcl_recv { # RATE LIMIT if (!vsthrottle.is_allowed(client.ip, 10, 10s)) { 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. nagłówka if (req.url == "/healthcheck" || req.http.X-Internal-Check) { return (pass); } # metody inne niż GET/HEAD bez cache if (req.method != "GET" && req.method != "HEAD") { return (pass); } # static – agresywnie cache’ujemy if (req.url ~ "^/static/" || req.url ~ "\.(css|js|png|jpg|svg|ico|woff2?)$") { return (hash); } return (hash); } sub vcl_backend_response { if (bereq.url ~ "^/static/" || bereq.url ~ "\.(css|js|png|jpg|svg|ico|woff2?)$") { set beresp.ttl = 24h; } 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 dla HTML/API } } } sub vcl_deliver { if (obj.hits > 0) { set resp.http.X-Cache = "HIT"; } else { set resp.http.X-Cache = "MISS"; } set resp.http.X-RateLimit-Limit = "10"; set resp.http.X-RateLimit-Window = "10s"; }