Help understanding routing with a custom `_routes.json`

Hey I'm debugging an user bug report for Astro. The user has the following _routes.json
{
"version": 1,
"include": [
"/404",
"/_image"
],
"exclude": []
}
{
"version": 1,
"include": [
"/404",
"/_image"
],
"exclude": []
}
And the following file-tree:
dist
├── _redirects
├── _routes.json
├── _worker.js
├── favicon.svg
└── index.html
dist
├── _redirects
├── _routes.json
├── _worker.js
├── favicon.svg
└── index.html
If he now tries to access his webpage using the following url: https://example.com/unknown, the index.html is returned. Would like to understand the why, so we can offer a fix in Astro?
7 Replies
Chaika
Chaika8mo ago
What's the question here? You said
If he now tries to access his webpage using the following url: https://example.com/unknown, the index.html.
If you're asking why the index.html is served when they access a page that doesn't exist? It's default pages serving assets behavior: https://developers.cloudflare.com/pages/platform/serving-pages/
If your project does not include a top-level 404.html file, Pages assumes that you are deploying a single-page application. This includes frameworks like React, Vue, and Angular. Pages’ default single-page application behavior matches all incoming paths to the root (/), allowing you to capture URLs like /about or /help and respond to them from within your SPA.
I'm guessing your advanced mode worker.js just passes through to env.ASSETS.fetch(request);? Which would go to that behavior
Serving Pages · Cloudflare Pages docs
Cloudflare Pages includes a number of defaults for serving your Pages sites. This page details some of those decisions, so you can understand how …
alex (he/him)
alex (he/him)8mo ago
Yeah that is the question. Why is index.html served? You are right it is the default behavior and we do pass through env.ASSETS.fetch() in the _worker.js, but given the _routes.json, the function should not be used. /unknown is not in the _routes.json array for include. But it still does somehow serve index.html. So get's index.html served by default if nothing else matches?
Chaika
Chaika8mo ago
hmm.. perhaps I'm missing something here, but I don't understand where the function ties into this? Are you saying the function is being invoked? It doesn't need the function to just return the index.html via SPA behavior, but yea the index.html is served by default if you don't have a 404.html and there's no other matches
alex (he/him)
alex (he/him)8mo ago
If the function is not invoked, the call to env.ASSETS.fetch() woudln't be called. But you say it does also serve index.html by default in SPA mode, without a function and call to env.ASSETS.fetch()
Chaika
Chaika8mo ago
yea you're right, sorry to confuse you there. It would indeed still serve it without the function being invoked though. You can create a 404.html in dist/ if you want it to serve a specific page when it's not found
alex (he/him)
alex (he/him)8mo ago
That's true. However our user request a server side rendered 404 🤷 So I think the best way is to somehow fix the _routes.json file, so that it uses a wildcard pattern to use the function to render out a 404 Very specific use-case...
Hello, I’m Allie!
Yeah. If I understand the _routes.json above, you are trying to get Functions to respond on a 404, which it doesn't support atm, so you would have to run it on every route