#109 Cloudflare Pages clean-URL redirects break the one-shape canonical URL contract
Problem
Cloudflare Pages rewrites HTML routes to extensionless URLs, while hu13 build emits .html links and hu13 serve/nginx serve them directly. The dist/ is therefore not one canonical shape: every generated page link redirects on Pages.
Verified reproduction
Wrangler 4.154.0 wrangler pages dev dist:
GET /issues/index.html -> 308 Location: /issues/
GET /files/a.txt.html -> 308 Location: /files/a.txt
GET /raw/evil.html -> 308 Location: /raw/evil
GET /releases/v1.html -> 308 Location: /releases/v1
GET /releases/v1/evil.html -> 308 Location: /releases/v1/evil
GET /issues/, /files/a.txt, /raw/evil, and /releases/v1 then return 200. A _redirects rule with status 200 cannot override the clean-URL redirect (verified).
This also makes the artifact redirect path security-relevant: #106 is the actual active-content bug, and the redirect is what changes the final URL.
Impact
- Internal navigation and feed links take a 308 on Pages only.
- Raw HTML downloads redirect on Pages only.
- External canonical URLs (
/issues/index.html) are not the Pages canonical URL. - The one-shape requirement is not met even though pages remain reachable.
Fix options
Pick during convergence: emit extensionless canonical links and adapt serve/docs, or add a Pages-side function/worker that preserves .html, or explicitly declare Pages redirect behavior acceptable and document the canonical URL difference. Do not rely on _redirects to suppress the platform redirect.
Uunknown ·
Resolved as documented platform behavior in PR #61 (
3df93c3a-bd5e-43f4-bb73-0f3dd886eeb1, commitee6e71b, mergede834a46). CI2026-10-27T19-50-04.907798288Zpassed.Pages clean-URL 308s are not suppressible via
_redirects200; the generated.htmllinks remain functional and Docker/nginx serve the exact path.docs/cloudflare.mdand.forge/wiki/deploynow state the Pages canonical form, and the artifact redirect remains download-only. This closes the issue as a documented Pages constraint rather than silently claiming byte-identical URLs.