React Router 本身是一个纯前端的库,它不直接“是”一个服务器。 要让 React Router 在现代 Web 应用中正常工作,特别是对于支持浏览器 URL(如 http://example.com/dashboard)的场景,服务器必须进行相应的配置和配合。

这种配合的核心是处理 “客户端路由”(Client-Side Routing) 和 “服务端渲染”(Server-Side Rendering, SSR)。
核心概念:客户端路由 vs. 服务端路由
在理解服务器如何配合之前,我们先要明白 React Router 解决的是什么问题。
传统服务端路由 (传统多页应用 - MPA)
- 工作方式:用户在浏览器中输入一个 URL (如
http://example.com/products),浏览器向服务器发送请求,服务器收到请求后,根据 URL 路径 (/products),找到对应的 HTML 文件,然后把这个完整的 HTML 文件返回给浏览器。 - 特点:每次 URL 变化,都会有一次完整的页面刷新,服务器负责“路由”和“渲染”。
- 缺点:用户体验有卡顿,每次交互都需要重新加载整个页面。
客户端路由 (现代单页应用 - SPA,React Router 属于此类)
- 工作方式:用户第一次访问应用时,服务器只返回一个最小的、包含所有 JavaScript 代码的 HTML 框架页面,之后,所有的路由切换(如从
/home切换到/about)都由浏览器内的 JavaScript 代码(React Router)来处理,它只更新页面中需要变化的部分,而不会刷新整个页面。 - 特点:用户体验流畅,像桌面应用一样。
- 问题:当用户直接在浏览器地址栏输入一个深层路径(如
http://example.com/dashboard/settings)或刷新一个非首页的 URL 时,如果服务器没有收到过对这个路径的请求,它会认为这个路径不存在,从而返回一个 404 Not Found 错误,因为服务器没有为/dashboard/settings这个路径准备一个 HTML 文件。
服务器的核心任务:处理客户端路由
为了解决上述问题,服务器需要扮演一个“智能中间人”的角色,无论用户请求的是 , /home, /dashboard, 还是 /dashboard/settings,服务器都应该做同一件事:返回应用的入口 HTML 文件(通常是 index.html)。
这个行为在技术上被称为 “回退”(Fallback) 或 “所有路由都指向 index.html”。

不同类型服务器的配置示例:
Node.js (Express)
这是最常见的场景,通常与 React Router 搭配使用。
// server.js
const express = require('express');
const path = require('path');
const app = express();
// 1. 静态文件服务 (如 public 目录下的 CSS, JS, 图片等)
app.use(express.static(path.join(__dirname, 'build')));
// 2. 所有其他请求都回退到 index.html
// 这是处理客户端路由的关键!
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
说明:
express.static用来提供生产环境打包后的静态资源。app.get('*', ...)是一个通配符路由,它捕获所有没有被静态文件中间件处理的 GET 请求(也就是所有 API 请求和页面路由请求),然后将index.html发送给浏览器,React Router 会在浏览器端接管这个 URL 并渲染正确的组件。
Nginx
Nginx 是一个高性能的 Web 服务器和反向代理。
# /etc/nginx/sites-available/your-app
server {
listen 80;
server_name your-domain.com;
# 指向你的 React 应用构建目录
root /var/www/your-app/build;
index index.html;
# 处理静态资源,并设置缓存
location /static/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# 这是处理客户端路由的关键!
# 将所有非静态资源的请求都回退到 index.html
location / {
try_files $uri /index.html;
}
# (可选) API 请求可以代理到后端服务
# location /api/ {
# proxy_pass http://backend-server;
# }
}
说明:

root和index指定了 Nginx 应该从哪里寻找文件。location /static/专门处理打包后的静态资源,提高性能。location / { try_files $uri /index.html; }是核心,它首先尝试请求用户访问的 URI ($uri),如果文件不存在(比如是/dashboard),它就回退到/index.html。
Apache
Apache 的配置也类似。
# .htaccess 文件 (放在你的 build 目录下)
# 启用重写模块
RewriteEngine On
# 如果请求的不是真实存在的文件或目录,则重写到 index.html
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
说明:
- 这个
.htaccess文件利用了 Apache 的mod_rewrite模块。 RewriteCond检查请求的路径是否不是一个文件 (!-f) 也不是一个目录 (!-d)。- 如果条件满足,
RewriteRule就将所有请求都重定向到index.html。
高级场景:服务端渲染 与 React Router
虽然上述“回退”方案能完美解决 SPA 的问题,但它有一个缺点:首屏加载速度和 SEO 可能不是最优的,因为浏览器需要先下载和解析 JavaScript,然后才能渲染出页面内容。
为了解决这个问题,我们可以采用 服务端渲染,在这种模式下,服务器会预先为用户请求的 URL 渲染出完整的 HTML 字符串,再发送给浏览器。
这时,服务器不仅需要“回退”,还需要一个“匹配器”(Matcher)。
SSR 服务器的核心任务:
- 静态资源服务:和之前一样,提供 CSS, JS 等文件。
- API 路由服务:如果应用有后端 API,服务器需要处理这些请求。
- 页面路由服务:这是 SSR 的关键。
- 当收到一个请求(如
/dashboard)时,服务器不能直接回退到index.html。 - 服务器需要运行一套与前端 完全相同 的 React Router 逻辑。
- 它根据请求的 URL (
/dashboard),找到对应的 React 组件。 - 在服务器上执行这个组件,将其渲染成 HTML 字符串。
- 将这个 HTML 字符串包裹在一个完整的 HTML 页面模板中,发送给浏览器。
- 当收到一个请求(如
- 回退处理:如果服务器找不到匹配的路由(比如用户访问了一个不存在的
/foo-bar),则返回 404 页面。
SSR 服务器架构示例 (使用 Express + react-router-dom)
// server.js (SSR 版本)
const express = require('express');
const React = require('react');
const { renderToString } = require('react-dom/server');
const { StaticRouter } = require('react-router-dom/server'); // 注意是 server 版本
const App = require('./src/App').default; // 你的根组件
const app = express();
// 静态资源服务
app.use(express.static('build'));
// 页面路由服务
app.get('*', (req, res) => {
// 1. 使用 StaticRouter 替换 BrowserRouter
// context 对象用于在渲染过程中传递数据,比如处理 <Link> 的重定向
const context = {};
const html = renderToString(
<StaticRouter location={req.url} context={context}>
<App />
</StaticRouter>
);
// 2. 将渲染的 HTML 插入到模板中
const template = `
<!DOCTYPE html>
<html>
<head>
<title>SSR React App</title>
</head>
<body>
<div id="root">${html}</div>
<script src="/bundle.js"></script> <!-- 注入客户端 JS -->
</body>
</html>
`;
// 3. 处理重定向(context 中有)
if (context.url) {
return res.redirect(301, context.url);
}
// 4. 发送最终 HTML
res.send(template);
});
app.listen(3000);
说明:
StaticRouter是react-router-dom专门为 SSR 提供的组件,它接收一个location属性(即服务器收到的 URL)。renderToString将 React 组件渲染成 HTML 字符串。- 服务器生成一个完整的 HTML 页面,并将渲染好的内容注入进去。
- 它仍然会注入客户端的 JavaScript bundle (
bundle.js),这样,当浏览器加载完 HTML 后,客户端的 React Router 会“接管”页面,后续的导航就变成了客户端路由,无需再请求服务器。
| 场景 | 服务器角色 | 核心配置/逻辑 | 适用情况 |
|---|---|---|---|
| 纯客户端渲染 | 智能回退服务器 | 所有未知路由 () 都返回 index.html。 |
简单的 SPA,对 SEO 和首屏加载速度要求不高的项目。 |
| 服务端渲染 | 路由匹配器 + 回退服务器 | 为已知 URL 渲染对应的 HTML。 2. 为未知 URL 返回 404 或回退到 index.html。 |
对 SEO 和首屏性能要求高的项目,如电商、博客、企业官网。 |
| 静态站点生成 | 静态文件服务器 | 直接部署构建出的 build 目录到任何静态托管服务(如 Netlify, Vercel, GitHub Pages)。 |
内容不经常变化的博客、文档、营销网站。 |
当你谈论“React Router 服务器”时,你实际上是在谈论一个能够正确处理 React Router 所需路由逻辑的 Web 服务器,对于绝大多数现代 React 应用,最基础和通用的要求就是配置服务器对所有路由进行回退,如果你的项目更高级,采用了 SSR,那么服务器就需要更复杂的路由匹配和渲染逻辑。
