Fix a whole site that 404s after a successful deploy
With base set, files sit at the root of dist/ but every URL starts with the base. Serve them from a matching directory.
Symptom
Section titled “Symptom”The deploy command exits zero and reports success. Every page on the live site returns 404 — the home page, every docs page, the CSS. Typically:
docs.jsonsetsbase, for example"base": "/docs";- the host’s asset or publish directory points straight at your
--outdirectory, such asdist/; https://example.com/docs/is a 404, whilehttps://example.com/index.html(no/docs) loads an unstyled page whose links all 404.
If a local static server pointed at dist/ shows the same thing, you are looking at this
problem, not a CDN or DNS issue.
base changes the URLs inside the HTML. It does not move files. A build with
base: "/docs" emits dist/index.html with every internal link and asset reference
prefixed /docs/..., but dist/index.html itself sits at the root of dist/.
Most static hosts serve their asset directory’s file tree verbatim as URL paths:
<directory>/a/b.html is served at /a/b.html. Nothing adds or strips a path segment.
Point that directory at dist/ and the tree contains no docs/ segment at all, so
/docs/ maps to nothing. The whole site 404s while the deploy reports success — the host
uploaded every file correctly, just at the wrong paths.
Routing does not rescue you. On a Cloudflare Worker, for example, routes decides which
requests reach the Worker; it never rewrites the path.
Make the mount path physically exist in the tree you serve. Build to dist/, then stage
it one level down, inside a directory named after your base:
stage/ docs/ <- the contents of dist/ index.html 404.html llms.txt _headers <- stays at the rootThen point the host at stage/, not dist/.
A staging script
Section titled “A staging script”This is the shape of the script Reed’s own documentation uses (scripts/build.sh):
set -euo pipefailMOUNT_SEGMENT="docs" # must match docs.json's base
rm -rf distreed build --out disttest -f dist/index.html
rm -rf stagemkdir -p "stage/$MOUNT_SEGMENT"cp -R dist/. "stage/$MOUNT_SEGMENT/"mv "stage/$MOUNT_SEGMENT/_headers" stage/_headersThree details matter:
dist/.as the copy source copies the contents ofdist/, not adistfolder.stage/is deleted and rebuilt on every run, so a page removed from a later build cannot survive as a stale file._headersgoes to the root ofstage/, not insidedocs/. Hosts read it only at the top of the served directory, and its rules match served URL paths — see Response headers. Move it rather than copy it: a copy left indocs/is served as a public file at/docs/_headers.
Point the host at the staged tree
Section titled “Point the host at the staged tree”For a Cloudflare static-assets Worker, set the asset directory in wrangler.jsonc:
{ "assets": { "directory": "./stage", "html_handling": "auto-trailing-slash", "not_found_handling": "404-page" }}not_found_handling: "404-page" is worth setting while you are here: the default returns
a bare 404 with an empty body instead of serving the 404.html Reed builds.
For other hosts, set the equivalent publish or output directory to stage — or, for
nginx, put the files in a docs/ directory under the server root. Keep the segment in
sync in three places: base in docs.json, the staging directory name, and any route
pattern your host uses.
Deploying a stale or absent stage/ is the other way to ship nothing. Run the build
script immediately before every deploy, not once.
Verify
Section titled “Verify”Check the tree before you upload it:
test -f stage/docs/index.html && test -f stage/_headers && echo "layout ok"After deploying, fetch the live site and check for a string from this build, not just the status code:
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/docs/curl -s https://example.com/docs/ | grep -c "a phrase you just added"Expect 200 and a count above zero. A deploy command exiting zero is not evidence that
the site is being served.
Related
Section titled “Related”- Deploy — mounting under a path
- site and base — what
basechanges and what it does not - Build output — the tree
reed buildwrites - Fix links that work in reed dev but 404 in production