Clean URLs and SPA routing from .deploy.yaml
Every static site eventually runs into the same two URL questions. Can /pricing serve pricing.html without the extension in the link? And can a single-page app with history-mode routing get a fallback so deep links stop 404ing?
Both are config keys in .deploy.yaml, the file fyra create leaves in your project directory. They’ve shipped quietly over the past few months, so here is the documentation.
Anything under config: is sent along with your next push and applied server-side. The file itself stays local; it never gets deployed with your site.
With clean_urls: true, extensionless paths resolve to their .html files:
- a request for
/pricingservespricing.html - a request for
/blog/hello-worldservesblog/hello-world.html
Your links can drop the .html everywhere. Requests for the explicit path keep working too, so existing bookmarks and cached pages are unaffected while you migrate at your own pace.
Single-page apps with history-mode routing (React Router, Vue Router, SvelteKit with the SPA adapter) need every unrecognized path to serve the app shell, so the client-side router can take over:
Any path that doesn’t match a real file now serves index.html. Point it at 200.html or /app/index.html if your build tool emits a dedicated shell; the value is any path within your site.
Set both and resolution goes in order:
- the exact path,
{path} - the directory index,
{path}/ - the clean-URL candidate,
{path}.html - the spa_redirect fallback
Real files always win. The fallback only catches paths with nothing to serve, so your assets, your images, and your generated pages are untouched while client-side routes fall through to the shell.
Edit .deploy.yaml, push, and the routing takes effect. Nothing to install, no CLI flags to remember.