Skip to main content

The 404 page and the SPA fallback

When a visitor opens a URL that is not in your build, Layero has to answer with something. There are two correct behaviours — and they are opposites:

  • A multi-page static site (Next.js output: 'export', Astro, Hugo, Eleventy, Jekyll and so on) usually puts a 404.html file in the build and expects an unknown address to return exactly that, with HTTP status 404. That is how search engines — Yandex, Google — learn the page is gone.
  • A SPA with client-side routing (React, Vue, Angular, Solid without prerendering) wants the opposite: any address should return index.html with status 200, so the app loads and its own router shows the right screen (or its own not-found state).

The default behaviour

Layero picks automatically, based on what is in the build:

Is there a 404.html at the build root?An unknown URL returns
Yes404.html with status 404
Noindex.html with status 200 (the SPA fallback)

Most projects need to do nothing: Next export, Astro, Hugo and similar generators write 404.html themselves, and it starts being served with the right status. Vite and CRA apps do not create 404.html, so they keep working as SPAs.

note

The 404.html file itself is always reachable at its direct address (/404.html) with status 200 — only unknown URLs get the 404.

Overriding through _redirects

If the automatic choice does not suit you, put a _redirects file (Netlify syntax) at the build root with a catch-all rule:

# Force the SPA fallback even when 404.html is present:
/* /index.html 200

# Or the other way round — explicitly turn on your own 404 page:
/* /404.html 404
  • /* /index.html 200always serve index.html with status 200. This is for apps with client-side routing that generate a 404.html anyway.
  • /* /404.html 404 — serve the given page with status 404. The path can be anything (/not-found.html and so on); the file has to exist in the build.

The rule must come last in _redirects — ordinary redirects (301/302) in the same file keep working as before.

Checking it

curl -I https://<your-project-address>/a-page-that-does-not-exist
# HTTP/2 404 ← expected for a static site with a 404.html
# HTTP/2 200 ← expected for a SPA

The change takes effect with the next deploy — Layero determines the 404 page at build time.