npm run dev 一切正常,部署到 Vercel / Netlify / Cloudflare Pages 后访问 /about 直接返回 404,或者只有首页能打开,其它路由全军覆没——这是 Astro 部署里最常见的一类陷阱。99% 的情况都不是 Astro 本身的 bug,而是 build.format、trailing slash、output 三者中至少一个和宿主的默认行为不对齐。本文把这三个变量讲清楚,并给一条可以照抄的修复路径。
常见原因
按命中率从高到低排。
1. build.format 和宿主的默认 trailing slash 不一致
Astro 的 build.format 决定每个页面输出 about/index.html(directory)还是 about.html(file)。Vercel / Netlify 默认期望 directory 形式并访问 /about;Cloudflare Pages 对两种都接受但行为略不同;某些自托管 nginx 则需要 file 形式。
具体表现:build 完产物里只有 dist/about.html,但宿主请求的是 /about/index.html,于是 404。
如何判断:ls dist/ 看是 about.html 还是 about/index.html,再对照宿主请求的 URL。
2. Trailing slash 重定向和 Astro 路由打架
宿主层加了 /about/ → /about 的 301(或反向),但你的 Astro 站只生成了带斜杠版本,请求被重定向到不存在的无斜杠版本,最终落到 404 页。
如何判断:curl -I https://yoursite.com/about 看是 200、301 还是 404,再 curl -I 一次重定向目标确认终点。
3. output: "server" 但宿主只接受静态产物
astro.config.mjs 里设了 output: "server" 或 output: "hybrid",build 出来的是带 SSR adapter 的产物。如果宿主(如 GitHub Pages、纯静态 S3)只能 serve 静态文件,所有动态路由直接 404,只有 index.html 能命中根路径。
如何判断:看 dist/ 里有没有 _worker.js / server/ 目录;有就是 SSR 产物。
4. 缺少 SPA fallback / _redirects
部分宿主需要显式的 public/_redirects 或 vercel.json rewrite 才能把所有未知路径回退到 index.html。Astro 静态站正常不需要,但你如果手写了 client-side router 就要。
如何判断:访问 /anything-that-doesnt-exist,如果直接 404 而不是回到首页,说明没有 fallback。
5. base 路径配置错
仓库部署到子路径(如 https://user.github.io/repo/)需要在 astro.config.mjs 设 base: "/repo",否则所有内部链接指向错误路径。
如何判断:打开浏览器 DevTools → Network,看 HTML 里资源路径是否带正确前缀。
最短修复路径
Step 1:本地 npm run build && npm run preview 复现 404
先把问题在本地复现,避免和宿主的 rewrite 层混在一起:
npm run build
npm run preview
# 然后开浏览器访问 http://localhost:4321/about
如果本地 preview 已经 404,说明是 Astro 配置问题;如果本地 OK、线上才 404,问题在宿主层。
Step 2:检查 dist/ 产物结构
ls dist/
# 期望:about/index.html (directory 模式)
# 或:about.html (file 模式)
对照 astro.config.mjs:
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
site: 'https://yoursite.com',
trailingSlash: 'always', // 'always' | 'never' | 'ignore'
build: {
format: 'directory', // 'directory' | 'file'
},
output: 'static', // 'static' | 'server' | 'hybrid'
});
| 宿主 | 推荐 build.format | trailingSlash |
|---|---|---|
| Vercel | directory | ignore |
| Netlify | directory | always |
| Cloudflare Pages | directory | ignore |
| GitHub Pages | directory | always |
| 纯静态 S3 + CloudFront | file | never |
Step 3:对齐宿主的 trailing slash 行为
Vercel 在 vercel.json 里:
{
"trailingSlash": false
}
Netlify 在 netlify.toml 里:
[build]
publish = "dist"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
确保宿主和 Astro 的 trailingSlash 值一致——两边都 always 或两边都 never,不要一边 always 一边 ignore。
Step 4:如果用了 output: "server",确认装了对应 adapter
# Vercel
npm install @astrojs/vercel
# Netlify
npm install @astrojs/netlify
# Cloudflare
npm install @astrojs/cloudflare
import vercel from '@astrojs/vercel/serverless';
export default defineConfig({
output: 'server',
adapter: vercel(),
});
没装 adapter 直接 output: "server" 部署,路由表生成不出来,所有动态路径都 404。
Step 5:清宿主的 deploy 缓存重新部署
改完配置后,宿主有时还会复用上次的 build 产物:
- Vercel:dashboard → Deployments → 最新 deploy → Redeploy → 取消勾选 “Use existing Build Cache”
- Cloudflare Pages:Settings → Builds & deployments → Purge cache
- Netlify:Deploys → Trigger deploy → “Clear cache and deploy site”
预防建议
- 把
build.format/trailingSlash/output三项以及宿主 rewrite 规则写进 README,新成员上手能直接对照 - 本地
npm run preview模拟生产构建,至少访问 3 条非首页路由(嵌套路由、动态路由、404 fallback) - CI 跑
npx astro check提前抓 routing 配置错误 - 部署后用
curl -I https://yoursite.com/some-page跑一遍冒烟测试,状态码不是 200 就报警 output: "server"切换前先确认宿主 adapter 已经装好且 build 产物里有_worker.js或server/
相关阅读
- Firebase 路由 404
- Cloudflare Pages 缓存陈旧
- canonical 写错会怎样
- RSS feed 返回 404:3 个原因 + 修复路径
- Astro adapter 与 SSR/SSG 模式不匹配 —— 排查与修复
- 部署 preview URL 被搜索引擎收录 —— 排查与修复
- GitHub Actions 部署步骤跑了 6 小时被强杀 —— 排查与修复
- monorepo 部署只发了一个 app —— 排查与修复
- Netlify Function 冷启动 10 秒超时 —— 排查与修复
- Service Worker 部署后还在发旧 bundle —— 排查与修复
- Vercel 构建超过 45 分钟被强杀 —— 排查与修复