【Java后端】【nginx】nginx配置重定向问题记录
Nginx 子路径部署 React SPA 踩坑总结
背景
将 React 单页应用(SPA)部署在 Nginx 子路径(/company/、/company-admin/)下,与个人博客(/)共用同一台服务器,过程中遇到了三个典型问题。
问题一:alias + try_files 回退路径错误
现象
刷新页面(如 /company/products)或直接访问子路由时,返回 404 或跳转到根路径。
错误配置
location /company/ {
alias /var/www/company-font/;
index index.html;
try_files $uri $uri/ /company/index.html; # ❌ 错误
}
根因
在 alias 块中,try_files 的回退路径 /company/index.html 是相对于全局 root(如 /var/www/personal-website)解析的,而不是 alias 指定的目录。该路径不存在,导致 Nginx 触发异常跳转。
正确配置
location /company/ {
alias /var/www/company-font/;
index index.html;
try_files $uri $uri/ /var/www/company-font/index.html; # ✅ 使用绝对路径
}
结论
在 alias 块中,try_files 的 fallback 必须使用文件系统绝对路径。
问题二:无尾斜杠路径 /company 被错误路由
现象
访问 http://example.com/company(无尾斜杠)时,返回了个人博客首页而不是企业官网。
根因
Nginx 的 location /company/(带尾斜杠)不匹配 /company(无尾斜杠)请求,该请求被 location / 兜底处理,返回了 /var/www/personal-website/index.html。
修复
新增精确匹配 location,将无尾斜杠请求 301 重定向到有尾斜杠路径:
location = /company {
return 301 /company/;
}
location = /company-admin {
return 301 /company-admin/;
}
问题三:React 登录后 navigate('/') 跳出子路径
现象
用户登录成功后,页面跳转到了 http://example.com/ 而不是 http://example.com/company/。
根因分析
React Router v6 中,BrowserRouter 设置了 basename="/company",navigate('/') 理论上会跳转到 /company/。但实际触发跳出的原因是:
- 旧代码在
navigate('/')之后紧接着调用了window.location.reload() navigate是异步的,reload()在路由未完成切换时执行,此时 URL 还在/company/login- reload 触发浏览器刷新,命中了上述问题二(
/company无斜杠被个人博客兜底),最终落到了根路径
修复
移除 window.location.reload(),改用 navigate 的 replace 选项:
// ❌ 旧代码
navigate('/');
window.location.reload();
// ✅ 新代码
navigate('/', { replace: true });
问题四:登录后 Navbar 状态不同步
现象
登录成功跳转回首页后,Navbar 仍然显示"登录/注册"按钮,没有变成用户信息。
根因
Navbar 组件读取登录状态的 useEffect 依赖数组为空,只在组件挂载时执行一次。登录操作在 Login 组件中完成,写入 localStorage 后 Navbar 不会感知到变化。
// ❌ 问题代码:只在挂载时读一次
useEffect(() => {
setLoggedIn(isAuthenticated());
setUser(getUser());
}, []);
修复
登录时派发自定义事件,Navbar 监听该事件刷新状态:
Login.jsx:
setToken(data.token);
setUser({ username, nickname, role });
window.dispatchEvent(new Event('auth-change')); // 通知 Navbar
navigate('/', { replace: true });
Navbar.jsx:
const refreshAuth = useCallback(() => {
setLoggedIn(isAuthenticated());
setUser(getUser());
}, []);
useEffect(() => {
refreshAuth();
window.addEventListener('auth-change', refreshAuth);
return () => window.removeEventListener('auth-change', refreshAuth);
}, [refreshAuth]);
最终完整 Nginx 配置
server {
listen 80;
server_name _;
root /var/www/personal-website;
index index.html;
client_max_body_size 20m;
# 无尾斜杠重定向,防止被 location / 兜底
location = /company {
return 301 /company/;
}
location = /company-admin {
return 301 /company-admin/;
}
# 个人博客(根路径)
location / {
try_files $uri $uri/ /index.html;
}
# 企业官网前端
location /company/ {
alias /var/www/company-font/;
index index.html;
try_files $uri $uri/ /var/www/company-font/index.html;
}
# 管理后台前端
location /company-admin/ {
alias /var/www/company-admin/;
index index.html;
try_files $uri $uri/ /var/www/company-admin/index.html;
}
# 后端 API(去掉前缀后转发)
location /company-api/ {
rewrite ^/company-api/(.*)$ /api/$1 break;
proxy_pass http://127.0.0.1:9080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
总结
| 问题 | 根因 | 修复要点 |
|---|---|---|
| alias + try_files 404 | fallback 路径相对于 root 解析 | 使用文件系统绝对路径 |
| 无尾斜杠被根路径兜底 | location /company/ 不匹配 /company |
新增 location = /company 做 301 跳转 |
| 登录后跳出子路径 | window.location.reload() 与异步 navigate 竞争 |
移除 reload,用 navigate('/', { replace: true }) |
| 登录后 Navbar 不更新 | useEffect 空依赖只执行一次 | 自定义事件 auth-change 跨组件通知 |
更多推荐




所有评论(0)