672 words
3 minutes

Astro Static Route 404 Behind Caddy: Align build.format and Trailing Slashes

2026-08-11
DevOps
Astro
/
Caddy
/
DevOps
/
Troubleshooting

If an Astro route works in development but returns 404 behind Caddy, inspect the generated dist/ tree before adding rewrites. A static route is only reachable when Astro’s output format, the URL’s trailing slash, Caddy’s document root, and the deployed files all describe the same path.

For most pre-rendered Astro sites, the simplest contract is build.format: 'directory', trailingSlash: 'always', and a Caddy file_server rooted at the directory that contains dist/. A route such as /guide/ then maps to dist/guide/index.html.

Inspect the generated path first#

Build the site and compare the failing URL with the actual files:

Terminal window
pnpm build
find dist -maxdepth 3 -type f | sort

Astro’s configuration reference defines these formats:

Astro settingExample outputURL contract to test
build.format: 'directory'dist/guide/index.html/guide/
build.format: 'file'dist/guide.html/guide.html, or an explicit host mapping
build.format: 'preserve'Follows the source file shapeTest each generated path directly

The default format is directory. Astro recommends pairing it with trailingSlash: 'always'; file pairs with trailingSlash: 'never'. For prerendered pages, the hosting server handles the final slash behavior, so a successful astro dev or astro preview request does not prove that Caddy will resolve the production path.

Use a consistent Astro configuration#

For a static site whose public URLs end in /, make the contract explicit:

astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
output: 'static',
trailingSlash: 'always',
build: {
format: 'directory',
},
});

This does not create redirects for a prerendered page by itself. It makes Astro generate a directory-shaped artifact and keeps development URLs aligned with the intended production shape. Your static server still needs to serve directories and their index.html files.

If you deliberately use build.format: 'file', do not expect /guide/ to find dist/guide.html. Either publish the extension-bearing URL or add a tested, explicit rewrite for the extensionless route. Switching to directory output is usually easier to reason about for a Caddy static site.

Point Caddy at the actual dist root#

The Caddy root must be the directory containing the generated files, not the repository parent or a directory that contains a second dist folder:

example.com {
root * /usr/share/caddy
file_server
}

In a container, copy the contents of dist into /usr/share/caddy:

FROM caddy:2-alpine
COPY dist/ /usr/share/caddy/

The existing Astro static site Docker and Caddy guide covers the multi-stage build boundary. This article focuses on the route contract after the files reach the server.

When the request is /guide/, Caddy’s static file server can resolve the directory and its index file. If you use try_files elsewhere in the site, Caddy’s documented directory form includes the trailing slash:

try_files {path} {path}/ =404

Do not replace a pre-rendered site’s real 404 handling with try_files {path} /index.html unless a client-side SPA router owns every unmatched route. That fallback can make a broken Astro path appear to work while returning the wrong document and canonical URL.

Test both slash variants and a real 404#

Run the checks against the same container or host configuration that serves production:

Terminal window
curl --head http://localhost:8080/guide/
curl --head http://localhost:8080/guide
curl --head http://localhost:8080/does-not-exist/

Interpret the results with the generated tree:

  • /guide/ should serve dist/guide/index.html for directory output.
  • /guide may redirect, depending on the server configuration; it should not be silently rewritten to an unrelated page.
  • A missing route should remain a real 404 rather than being swallowed by an SPA fallback.

If the site is deployed below a subpath such as /docs, also set Astro’s base option and verify that assets are copied under that prefix. A correct Caddy root cannot compensate for links generated with the wrong base path.

FAQ#

Q: Why does /guide/ work locally but 404 in Caddy?#

A: Local preview knows Astro’s route model, while Caddy sees only files. Check whether dist/guide/index.html exists and whether Caddy’s root points directly at the directory containing it.

Q: Should I add a Caddy SPA fallback for every Astro 404?#

A: No. A pre-rendered Astro site should serve its generated files and return 404 for missing routes. Use an SPA fallback only when a client-side router intentionally owns those paths.

Q: Is trailingSlash enough to configure a static host?#

A: No. Astro documents the URL preference, but prerendered page slash handling is performed by the hosting platform. Align the setting with build.format and verify the host with curl.

References:

Astro Docs: Configuration Reference

Caddy Docs: try_files

Caddy Docs: Common Caddyfile Patterns

Caddy Docs: file_server

Astro Static Route 404 Behind Caddy: Align build.format and Trailing Slashes
https://laplusda.com/en/posts/astro-caddy-static-route-404/
Author
Zero
Published at
2026-08-11
License
CC BY-NC-SA 4.0
Was this article useful?

Report a typo or broken link, or suggest a related topic.