如何修复网站或应用部署后出现的 404 错误
如果你的应用在本地运行正常,但部署后页面返回 404,最常见的原因是缺少单页应用(SPA)的路由配置:服务器去查找一个名为 /dashboard 的文件,而这个文件并不存在,因为该路由只存在于 JavaScript 中。解决方法是添加一条回退规则,为应用路由返回 index.html。其他原因包括构建产物中缺少文件、路径大小写敏感、末尾斜杠不一致、基础路径(base path)设置错误,以及从未部署过的 API 路由。
首先记下具体是哪个 URL 返回了 404,以及你是如何访问到它的。出现的规律会直接指向原因。
第一步:找出规律
| 你看到的现象 | 最可能的原因 | 跳转到 |
|---|---|---|
| 首页正常,点击链接也正常,但刷新或直接打开深层链接时返回 404 | 没有 SPA 回退配置 | 第二步 |
| 所有页面都返回 404,包括首页 | 发布了错误的文件夹,或构建没有成功部署 | 第三步 |
| 页面能加载,但图片、字体或脚本返回 404 | 构建中缺少文件,或路径错误 | 第四步 |
/about/ 正常,/about 报错(或反过来) | 末尾斜杠的处理方式不一致 | 第五步 |
/About 报错,但 /about 正常 | 服务器区分大小写 | 第四步 |
页面能加载,但 /api/... 的数据请求返回 404 | API 未部署或未配置路由 | 第六步 |
| 旧站点的 URL 返回 404 | 缺少重定向 | 第七步 |
| 只有在自定义域名下出现 | DNS 或域名设置问题 | 第八步 |
要查看真实的状态码,请打开开发者工具(右键 → 检查),切换到 网络 标签页,刷新页面,然后查看第一个请求的状态。
第二步:添加 SPA 回退
这是最经典的问题。在 React、Vue 或 Svelte 的单页应用中,服务器上只有少量真实文件:index.html 以及一些 JavaScript 和 CSS。/dashboard 或 /projects/42 这样的路由由浏览器中的路由器处理。
- 点击链接可以正常工作:路由器修改 URL 并渲染新页面,无需向服务器请求。
- 刷新或直接打开链接会失败:浏览器向服务器请求
/dashboard,服务器没有这个文件,于是返回 404。
解决方法是添加一条规则:如果请求与真实文件不匹配,就以 200 状态码返回 index.html,交由路由器接管。具体如何添加取决于托管平台(请查看你所用平台的最新文档,因为细节会变化):
| 托管平台 | 常见的回退规则 |
|---|---|
| Netlify | 在发布文件夹中添加 _redirects 文件,内容为 /* /index.html 200 |
| Vercel | 在 vercel.json 中添加重写规则,将所有路径指向 /index.html(框架预设通常会自动处理) |
| Cloudflare Pages | 没有顶层 404.html 时,默认提供 SPA 风格的回退 |
| Nginx | 在 location / 块中添加 try_files $uri $uri/ /index.html; |
| Apache | 在 .htaccess 中添加重写规则,将非文件请求发送到 index.html |
| GitHub Pages | 不支持服务器端重写;常见的变通方法是使用哈希路由,或用一个会跳转的 404.html |
确保这条规则不会吞掉真实文件:对 /assets/app.js 的请求仍然必须返回 JavaScript 文件。如果它返回的是 index.html,你就会看到一个空白页面,并伴随“Failed to load module script”错误。参见如何修复白屏问题。
还应为路由器设置一个兜底路由,这样不匹配任何页面的 URL 会显示友好的“页面未找到”提示,而不是什么都没有。
如果你无法修改服务器设置,可以使用哈希路由(如 /#/dashboard 这样的 URL),这样就能避免该问题,因为 # 后面的部分永远不会发送到服务器。这只是一种变通方案,而不是首选:URL 看起来不够美观,对搜索引擎也不太友好。
在服务器端渲染或构建时生成页面的框架(如 Next.js、Astro 等),通常会为每个页面提供真实文件或服务器路由,因此这个问题在它们那里较少出现。它们对应的情况是某个页面在构建时从未被生成。
第三步:检查实际部署的内容
如果所有页面都返回 404,那么服务器很可能没有提供你构建好的应用。
- **正确的文件夹。**托管平台必须发布构建产物(Vite 通常是
dist,Create React App 是build,静态 Next.js 导出是out),而不是项目根目录。 - **构建确实运行了。**查看部署日志。如果构建失败,托管平台可能什么都没部署,或者只部署了旧版本。
index.html位于发布文件夹的顶层,而不是某个子文件夹中。- **基础路径匹配。**如果应用部署在
example.com/app/下,就必须以该基础路径进行构建(Vite 的base,React Router 的basename),否则所有路由和资源都会找不到。
第四步:修复缺失的文件和错误的路径
当页面能加载但资源返回 404 时:
- 大小写敏感。 macOS 和 Windows 通常将
Logo.png和logo.png视为同一个文件,而大多数 Linux 服务器不会。请确保每个引用都与实际文件名完全一致。 - **构建之外的文件。**在 Vite 中,
public/目录下的文件会原样复制,并从根路径引用(/logo.png)。在代码中导入的文件会被打包。位于项目其他位置的文件可能根本没有被部署。 - 相对路径。
images/photo.jpg会相对于当前 URL 解析,因此在/blog/post-1上会失效。应改用/images/photo.jpg。 - **上传的文件保存在本地磁盘。**如果用户上传的文件被写入服务器自身的文件夹,那么新版本部署时它们就会消失。请使用正规的文件存储服务。
第五步:统一处理末尾斜杠
/about 和 /about/ 是两个不同的 URL。不同的托管平台对于是否添加或去掉斜杠的处理方式不同,如果它与你的链接或路由器不一致,就可能产生 404 或重定向循环。请选定一种风格,让你的链接都使用这种风格,并配置托管平台把另一种形式重定向过来。统一风格也有助于搜索引擎,否则它们可能会把每个页面看作两份副本。
第六步:修复返回 404 的 API 路由
如果页面能加载,但网络标签页中显示 /api/... 请求返回 404:
- **后端未部署。**静态托管平台只提供文件服务,后端必须在某处运行。
- **生产环境中的路由方式不同。**在开发环境中,代理可能会把
/api转发到另一台服务器。在生产环境中,托管平台需要等效的重写配置,或者前端必须直接调用 API 的真实地址。 - **SPA 回退拦截了请求。**如果某个 API 请求以 200 状态码返回了
index.html,说明回退规则太宽泛了。请将/api/*排除在回退规则之外。
已发布应用对 /api/orders 的请求返回 404,但在预览中可以正常工作。请检查预览与已发布应用中 /api 的路由方式有何不同,并让已发布的前端以相同的方式访问后端。
第七步:重定向旧 URL
如果你从其他网站迁移过来,或者重新调整了网站结构,那么人们和搜索引擎仍在使用的旧 URL 就会返回 404。请为每个旧 URL 添加一条指向新地址的 301(永久)重定向。保留一份清单,并在上线后抽样测试。我们关于迁移 WordPress 网站的指南介绍了如何整理这份清单。
第八步:检查域名
如果应用在托管平台的地址上能正常运行,但在你的自定义域名上返回 404,那么可能是域名指向了错误的项目,或者托管平台尚未将该域名绑定到你的站点。请检查 DNS 记录和托管平台的域名设置。参见如何连接自定义域名。
让真正的 404 页面变得有用
有些 404 是正常的:有人输错了 URL。请让该页面变得有帮助:
- 一段简短的说明,以及指向首页和主要板块的链接
- 如果网站有搜索功能,提供一个搜索框
- 与网站其他部分相同的页头和导航
在你的配置允许的情况下,对于不存在的页面应返回真正的 404 状态码。以 200 状态码返回的“未找到”信息,正是搜索引擎所说的软 404,它会浪费搜索引擎的抓取时间,并干扰索引。
常见错误
- **只通过点击来测试。**应始终将深层 URL 粘贴到新标签页中并刷新进行测试。
- **回退规则覆盖了一切。**资源和 API 路由返回
index.html,导致空白页面和令人困惑的 API 错误。 - 大小写不一致,只在服务器上才会出问题。
- 部署了源代码文件夹,而不是构建产物。
- **调整 URL 结构却没有设置重定向。**每个旧链接都会变成 404。
Mythex 中的 404 问题
在 Mythex 中,你从工作区发布,静态前端会作为静态站点部署。如果已发布的页面出现 404,请把确切的 URL、问题是在刷新时还是点击时出现,以及网络标签页显示的内容告诉智能体,并请它检查路由配置。修复后请重新发布,因为在你重新发布之前,线上应用只是一个快照。如果你最近修改了项目,但线上站点看起来仍是旧版本,请重新发布并强制刷新。如果构建失败,可以在发布对话框中使用用 AI 修复。相关说明可参阅文档中的如何发布和发布故障排查,也可以参阅我们关于调试 AI 搭建应用的指南。
404 检查清单
- 已记下具体哪些 URL 返回 404,以及是点击时、刷新时还是两者都出现
- SPA 回退为应用路由以 200 状态码返回
index.html - 回退规则排除了真实文件和
/api/* - 路由器设置了兜底的“未找到”路由
- 发布的是构建产物文件夹,且构建已成功
- 基础路径与应用的部署位置一致
- 文件名的大小写与每个引用完全一致
- 资源路径以
/开头 - 末尾斜杠风格保持一致
- API 路由已在生产环境中部署并配置好路由
- 旧 URL 已设置 301 重定向
- 已将深层链接粘贴到新标签页中测试过
常见问题
为什么我的 React 应用刷新页面时会出现 404?
在单页应用中,像 /dashboard 这样的路由只存在于 JavaScript 中。点击链接之所以正常,是因为路由器在浏览器中处理了它;而刷新页面时,浏览器会向服务器请求一个并不存在的 /dashboard 文件。解决方法是添加一条回退规则,为应用路由返回 index.html。
如何在 Netlify、Vercel 或 Nginx 上添加 SPA 回退?
在 Netlify 上,添加一个 _redirects 文件,内容为 /* /index.html 200。在 Vercel 上,在 vercel.json 中添加重写规则,把所有路径指向 /index.html。在 Nginx 上,在 location 块中使用 try_files $uri $uri/ /index.html;。由于规则会变化,请以你所用平台的文档为准。
部署后图片或文件出现 404,是什么原因?
通常是因为文件不在构建产物中、路径的大小写与实际不一致(服务器往往区分大小写,而你的笔记本电脑不区分),或者路径应当以斜杠开头却使用了相对路径。
不存在的页面是否应该返回真正的 404 状态码?
应该。在你的配置允许的情况下,既要展示有用的未找到页面,也要返回 404 状态码。搜索引擎会把以 200 状态码返回的“未找到”页面视为软 404,这会浪费抓取预算,也可能干扰索引。