vcl 4.1; # Backend definition - points to our PHP/Apache container backend default { .host = "app"; .port = "80"; .connect_timeout = 60s; .first_byte_timeout = 60s; .between_bytes_timeout = 60s; } # Called at the beginning of a request sub vcl_recv { # Remove port from Host header for proper backend routing if (req.http.Host) { set req.http.Host = regsub(req.http.Host, ":[0-9]+", ""); } # Handle image requests if (req.url ~ "\.(jpg|jpeg|png|gif|webp|svg|ico)$") { # Cache images aggressively set req.http.Cache-Control = "public, max-age=2592000"; return (hash); } # Handle API image proxy requests if (req.url ~ "^/api/image\.php") { # Cache external images through our proxy return (hash); } # Handle CSS and JS files if (req.url ~ "\.(css|js)$") { return (hash); } # Don't cache POST, PUT, DELETE requests if (req.method != "GET" && req.method != "HEAD") { return (pass); } # Don't cache requests with cookies (except for images) if (req.http.Cookie && req.url !~ "\.(jpg|jpeg|png|gif|webp|svg|ico|css|js)$") { return (pass); } # Remove tracking parameters if (req.url ~ "\?") { set req.url = regsub(req.url, "\?.*", ""); } return (hash); } # Called after a cache lookup sub vcl_hit { # Add cache hit header for debugging set req.http.X-Cache = "HIT"; return (deliver); } # Called after a cache miss sub vcl_miss { set req.http.X-Cache = "MISS"; return (fetch); } # Called before sending response to client sub vcl_deliver { # Add cache status header if (req.http.X-Cache) { set resp.http.X-Cache = req.http.X-Cache; } # Add CORS headers for images if (req.url ~ "\.(jpg|jpeg|png|gif|webp|svg|ico)$") { set resp.http.Access-Control-Allow-Origin = "*"; } # Remove server identification unset resp.http.Server; unset resp.http.X-Powered-By; unset resp.http.X-Varnish; return (deliver); } # Called after fetching from backend sub vcl_backend_response { # Cache images for 30 days if (bereq.url ~ "\.(jpg|jpeg|png|gif|webp|svg|ico)$") { set beresp.ttl = 30d; set beresp.http.Cache-Control = "public, max-age=2592000"; set beresp.http.Expires = beresp.http.Date + 30d; # Don't cache if backend says not to if (beresp.http.Cache-Control ~ "no-cache") { set beresp.ttl = 0s; } } # Cache CSS and JS for 7 days if (bereq.url ~ "\.(css|js)$") { set beresp.ttl = 7d; set beresp.http.Cache-Control = "public, max-age=604800"; } # Cache API image proxy for 1 day if (bereq.url ~ "^/api/image\.php") { set beresp.ttl = 1d; set beresp.http.Cache-Control = "public, max-age=86400"; } # Don't cache if there's an error if (beresp.status >= 400) { set beresp.ttl = 0s; set beresp.uncacheable = true; } # Enable compression if (beresp.http.Content-Type ~ "text|application|image/svg") { set beresp.do_gzip = true; } return (deliver); } # Called when there's an error sub vcl_backend_error { set beresp.http.Content-Type = "text/html; charset=utf-8"; set beresp.status = 503; synthetic( {" Service Unavailable

Service Temporarily Unavailable

Please try again in a few moments.

"} ); return (deliver); }