From 7e8353a100109e0a362ffd1663311fdc065b6a08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Fri, 4 Sep 2026 20:32:27 +0100 Subject: [PATCH] serve the page at the address that was asked for MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit try_files listed $uri/ before any index, so nginx answered /gallery/aldi/65 with a 301 appending the slash. Every internal link on this site is the bare form — the thumbnails, the lightbox hrefs, the chevrons — so every navigation paid a redirect before anything on the page could start. Lighthouse measured it at 78 ms on the critical path of the first request. Naming index.html first serves the file directly. Both forms still work, neither redirects, and $uri/ stays after it for a directory with no index. Co-Authored-By: Claude Opus 5 (1M context) --- api/static-site.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/api/static-site.ts b/api/static-site.ts index 41ae6e3..160a4c5 100644 --- a/api/static-site.ts +++ b/api/static-site.ts @@ -306,7 +306,16 @@ const NGINX_CONF = `server { location / { # The build writes about/index.html, so a directory match is what serves /about. - try_files $uri $uri/ =404; + # + # $uri/index.html BEFORE $uri/, and the order is the whole point. A $uri/ match is a directory + # and nginx answers it with a 301 that appends the slash — so /gallery/aldi/65 cost a redirect + # before anything on the page could start. Every internal link on this site is the bare form (the + # thumbnails, the lightbox, the chevrons), so every navigation paid it: 78 ms on the critical path, + # measured, on the first request of every page. + # + # Naming index.html first serves the file at the address that was asked for. Both forms still work + # and neither redirects; $uri/ stays after it for a directory that has no index. + try_files $uri $uri/index.html $uri/ =404; add_header Cache-Control "no-cache"; } }