Astro 部署后页面 404:3 个原因 + 修复路径

dev 正常、线上 404——`build.format` / trailing slash / output mode 三者要对齐。

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/_redirectsvercel.json rewrite 才能把所有未知路径回退到 index.html。Astro 静态站正常不需要,但你如果手写了 client-side router 就要。

如何判断:访问 /anything-that-doesnt-exist,如果直接 404 而不是回到首页,说明没有 fallback。

5. base 路径配置错

仓库部署到子路径(如 https://user.github.io/repo/)需要在 astro.config.mjsbase: "/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.formattrailingSlash
Verceldirectoryignore
Netlifydirectoryalways
Cloudflare Pagesdirectoryignore
GitHub Pagesdirectoryalways
纯静态 S3 + CloudFrontfilenever

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.jsserver/

相关阅读

标签: #部署 / 托管 #排查 #排查 #Astro #路由 404