From aaa786fcea8de25be1bd2eff8bc35afd1bc7482a Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sun, 26 Jul 2026 09:39:15 +0300 Subject: frontends: stop relayd clobbering cache headers for every f3s service The three cache-control response rules were unscoped, so relayd rewrote every response passing through the https relay -- not just the fallback pages the comment described. Everything the k3s cluster serves was forced to "no-cache, no-store, must-revalidate", overriding whatever the backend set. cgit, for example, marks its CSS and logo "expires 30d", but browsers re-fetched them on every page view. relayd cannot filter a response by the backend table that produced it, and a "header set" cannot be combined with a header match in the same rule. So match the Server header -- the local httpd is the only backend answering "OpenBSD httpd" -- and carry that across with a sticky tag. Verified on blowfish before rollout with an isolated relayd instance: a response from httpd:8080 gets the tagged headers, one from the cluster's nginx does not. Fallback behaviour retested after deploy: c-git.f3s.buetow.org served from httpd:8080 still returns the "Server turned off" page on any path, with all three headers intact. cgit.css now passes through max-age=2592000. Co-Authored-By: Claude Opus 5 --- frontends/etc/relayd.conf.tpl | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/frontends/etc/relayd.conf.tpl b/frontends/etc/relayd.conf.tpl index b8d7a24..02b4887 100644 --- a/frontends/etc/relayd.conf.tpl +++ b/frontends/etc/relayd.conf.tpl @@ -122,10 +122,25 @@ http protocol "https" { match request header "Host" value "<%= $prefix.$host -%>" forward to <% } } -%> - # Add cache-control headers to f3s fallback pages (served from localhost when cluster is down) - match response header set "Cache-Control" value "no-cache, no-store, must-revalidate" - match response header set "Pragma" value "no-cache" - match response header set "Expires" value "0" + # Keep the f3s fallback pages (served from localhost httpd when the cluster + # is down) out of browser caches, so nobody keeps seeing "cluster is down" + # after the cluster recovers. + # + # These rules used to be unscoped, so they rewrote EVERY response through + # this relay -- including everything the k3s cluster serves. That silently + # defeated upstream cache headers cluster-wide: cgit sets "expires 30d" on + # its CSS and logo, but browsers re-fetched them on every page view. + # + # relayd cannot filter a response by the backend table that produced it, so + # scope on the Server header instead: the local httpd is the only backend + # that answers "OpenBSD httpd" (the cluster answers nginx, the Pis + # bozohttpd). A "header set" cannot be combined with a header match in one + # rule, hence the tag: it is sticky for the connection, and a later + # response rule picks it up with "tagged". + match response header "Server" value "OpenBSD httpd" tag "HTTPD_FALLBACK" + match response tagged "HTTPD_FALLBACK" header set "Cache-Control" value "no-cache, no-store, must-revalidate" + match response tagged "HTTPD_FALLBACK" header set "Pragma" value "no-cache" + match response tagged "HTTPD_FALLBACK" header set "Expires" value "0" } relay "https4" { -- cgit v1.2.3