Files
plikd_docker/varnish/default.vcl
Mateusz Gruszczyński 6cfcf7cd6f rebuild
2025-09-25 12:22:02 +02:00

217 lines
6.9 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

vcl 4.1;
import std;
import 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 { "localhost"; "127.0.0.1"; "::1"; }
sub vcl_recv {
# --- anty-noise / normalizacja ---
unset req.http.X-Cache;
unset req.http.X-Cache-Hits;
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, "\?$", "");
set req.http.Surrogate-Capability = "key=ESI/1.0";
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; }
}
# --- RATE LIMIT: 100/10s, kara 10s ---
if (vsthrottle.is_denied(client.identity, 100, 10s, 10s)) {
return (synth(429, "Too Many Requests"));
}
# --- metody administracyjne ---
if (req.method == "PURGE") {
if (!client.ip ~ purge) { return (synth(405, "Not allowed.")); }
return (hash);
}
if (req.method == "BAN") {
if (!client.ip ~ purge) { return (synth(405, "Not allowed.")); }
ban("req.http.host == " + req.http.host + " && req.url ~ " + req.url);
return (synth(200, "Banned"));
}
# --- dopuszczalne metody / pass dla niecacheowalnych ---
if (req.method != "GET" && req.method != "HEAD" && req.method != "OPTIONS") {
return (pass);
}
if (req.http.Authorization) { return (pass); }
# --- wyjątki dynamiczne (np. admin, ajax, status) ---
if (req.url ~ "(?i)/(ajax|ahah)/") {
return (pass);
}
# --- Accept-Encoding (nie kompresujemy oczywistych statyk po rozszerzeniu) ---
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 ~ "gzip") {
set req.http.Accept-Encoding = "gzip";
} elseif (req.http.Accept-Encoding ~ "deflate" && req.http.user-agent !~ "MSIE") {
set req.http.Accept-Encoding = "deflate";
} else {
unset req.http.Accept-Encoding;
}
}
# --- cookies: tylko jeśli naprawdę potrzebne do cache key; inaczej wyczyść ---
if (req.http.Cookie) {
set req.http.Cookie = ";" + req.http.Cookie;
set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
# przykładowy whitelist (dopasuj pod aplikację); tu czyścimy wszystkie
set req.http.Cookie = regsuball(req.http.Cookie, ";[^;]*", "");
set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");
if (req.http.Cookie ~ "^\s*$") { unset req.http.Cookie; }
else { return (pass); } # zostało coś istotnego → omijamy cache
}
return (hash);
}
sub vcl_hash {
hash_data(req.http.X-Forwarded-Proto);
}
sub vcl_hit {
set req.http.X-Cache = "HIT";
if (obj.ttl <= 0s && obj.grace > 0s) { set req.http.X-Cache = "HIT-GRACE"; }
}
sub vcl_miss { set req.http.X-Cache = "MISS"; }
sub vcl_pass { set req.http.X-Cache = "PASS"; }
sub vcl_backend_response {
# krótkie TTL dla wybranych statusów
if (beresp.status == 404 || beresp.status == 301 || beresp.status == 500) {
set beresp.ttl = 10m;
}
# retry na 5xx (bez pętli)
if (beresp.status == 500 || beresp.status == 503) { return (retry); }
# kompresja
if (bereq.url ~ "(?i)\.(jpg|jpeg|png|gif|webp|ico|svg|mp4|mp3|pdf|zip|7z|gz|bz2)$") {
set beresp.do_gzip = false;
} else {
set beresp.do_gzip = true;
}
# TTL: honoruj Cache-Control; no-store/private = 0
if (beresp.http.Cache-Control ~ "(?i)no-store|private") {
set beresp.ttl = 0s;
} else {
if (beresp.http.Cache-Control ~ "(?i)s-maxage=\d+") {
set beresp.ttl = std.duration(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);
}
# fallback (lekko agresywny, prosto)
if (beresp.ttl <= 0s) {
if (beresp.http.Content-Type ~ "(?i)^image/|^font/|/javascript|/css") { set beresp.ttl = 7d; }
elseif (beresp.http.Content-Type ~ "(?i)^text/|^application/json") { set beresp.ttl = 1d; }
else { set beresp.ttl = 1h; }
}
}
# usuń ciasteczka dla statyk/binariów
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;
}
# ESI
if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
unset beresp.http.Surrogate-Control;
set beresp.do_esi = true;
}
# grace/keep
if (beresp.ttl > 0s) {
set beresp.grace = beresp.ttl / 10;
if (beresp.grace < 10m) { set beresp.grace = 10m; }
if (beresp.grace > 2h) { set beresp.grace = 2h; }
if (beresp.ttl > 1h) {
set beresp.keep = 1h;
} else {
set beresp.keep = beresp.ttl;
}
} else {
set beresp.grace = 0s;
set beresp.keep = 0s;
}
# streaming dużych odpowiedzi (>1 MiB)
if (beresp.http.Content-Length && std.integer(beresp.http.Content-Length, 0) > 1048576) {
set beresp.do_stream = true;
}
}
sub vcl_deliver {
# polityka Cache-Control po typie (frontend)
if (resp.http.Content-Type ~ "(?i)^image/|^font/|/javascript|/css") {
set resp.http.Cache-Control = "public, max-age=604800"; # 7d
} elseif (resp.http.Content-Type ~ "(?i)^text/|^application/json") {
set resp.http.Cache-Control = "public, max-age=86400"; # 1d
}
unset resp.http.Expires;
unset resp.http.Pragma;
# metryki cache
if (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 {
set resp.http.X-Cache = "MISS";
unset resp.http.Age;
}
# twarde usunięcie sygnatur serwera
unset resp.http.X-Url;
unset resp.http.X-Host;
unset resp.http.Via;
unset resp.http.X-Varnish;
unset resp.http.Server;
set resp.http.X-Frame-Options = "SAMEORIGIN";
# strona serwisowa dla wybranych statusów
if (resp.status == 403 || resp.status == 404 || resp.status == 500 || resp.status == 503) {
return (synth(800, "Maintenance page"));
}
}
sub vcl_synth {
set resp.http.X-Cache = "SYNTH";
if (resp.status == 503 && req.restarts < 4) { return (restart); }
if (resp.status == 800) {
set resp.http.Content-Type = "text/html; charset=utf-8";
set resp.status = 404;
set resp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
synthetic({"<!DOCTYPE html>
<html><head><title>"} + resp.status + " " + resp.reason + {"</title></head>
<body><h1>Error "} + resp.status + {"</h1><p>"} + resp.reason + {"</p></body></html>"});
return (deliver);
}
}