From 2ee53fe3758cbeac769b005c0774b040158df2c6 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Wed, 29 Apr 2026 15:16:44 -0700 Subject: [PATCH] =?UTF-8?q?C0:=20fix=20Caddyfile=20try=5Fhtml=20=E2=80=94?= =?UTF-8?q?=20handle=5Ferrors=20can't=20nest=20inside=20handle{}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kind=static branch added in #342 put handle_errors inside the @host handle{} block. handle_errors is a top-level site-block directive, not an ordered HTTP handler, so Caddy refuses to load the config: parsing caddyfile tokens for 'handle': directive 'handle_errors' is not an ordered HTTP handler This crash-loops the whole reverse proxy and takes down every *.ops.eblu.me service. Tripped today during the live cv/docs cutover. Fix: drop handle_errors and append /404.html as the final try_files candidate. The 404 page is served with status 200 instead of 404, but that's acceptable for a human-facing curated 404 — the page renders correctly. Documented inline. The running Caddy on indri already has the fixed config (deployed manually during the cutover); this lands the fix in main so future provision-indri --tags caddy runs don't re-break it. Co-Authored-By: Claude Opus 4.7 (1M context) --- ansible/roles/caddy/templates/Caddyfile.j2 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ansible/roles/caddy/templates/Caddyfile.j2 b/ansible/roles/caddy/templates/Caddyfile.j2 index b08f16a..f6b5f64 100644 --- a/ansible/roles/caddy/templates/Caddyfile.j2 +++ b/ansible/roles/caddy/templates/Caddyfile.j2 @@ -42,11 +42,11 @@ header @{{ service.name }}_dl{{ loop.index }} Content-Disposition `attachment; filename="{{ dl.filename }}"` {% endfor %} {% if service.try_html | default(false) %} - try_files {path} {path}/ {path}.html - handle_errors 404 { - rewrite * /404.html - file_server - } + # Quartz clean URLs: path → path/ → path.html → /404.html (200). + # Caddy's handle_errors is a top-level directive and can't live in + # this nested handle, so the 404 page rides as the final try_files + # candidate (served with 200 — acceptable for a human-facing 404). + try_files {path} {path}/ {path}.html /404.html {% endif %} file_server {% else %}