Next.js 静态导出应用在 Nginx 上路由失效的原因及解决方案
问题现象
当我们使用 Next.js 的静态导出功能构建应用后,部署到 Nginx 服务器上时,可能会遇到以下问题:
- 直接访问首页正常显示
- 刷新页面或直接访问非首页路由时出现 404 错误
- 浏览器前进/后退按钮在某些路由下失效
原因分析
这个问题的根本原因在于 Next.js 的静态导出机制和 Nginx 的默认路由处理方式之间的差异。
Next.js 静态导出的工作原理
Next.js 的静态导出会在构建过程中预渲染所有页面,并生成对应的 HTML 文件。例如:
- 首页 (/) 会生成 index.html
- 关于页面 (/about) 会生成 about/index.html
- 详情页 (/post/123) 会生成 post/123/index.html
Nginx 的默认路由行为
Nginx 作为 Web 服务器,默认情况下会尝试查找与 URL 路径完全匹配的静态文件。当请求一个不存在的文件时,它会返回 404 错误。
例如,当用户访问 /about 时:
- Nginx 会尝试查找 /about 文件
- 由于实际存在的是 /about/index.html,所以找不到文件
- 返回 404 错误
解决方案
要解决这个问题,我们需要配置 Nginx 使其能够正确路由到 Next.js 生成的静态文件。
方案1:使用 try_files 指令
这是最常用的解决方案,通过配置 Nginx 的 try_files 指令来按顺序尝试不同的文件路径。
server {
listen 80;
server_name your-domain.com;
root /path/to/your/nextjs/app;
# 主要配置:尝试文件,然后尝试目录,最后 fallback 到 index.html
location / {
try_files $uri $uri/ /index.html;
}
# 可选:处理静态资源缓存
location /_next/static {
expires 365d;
access_log off;
}
}配置说明:
$uri:尝试直接访问请求的文件$uri/:尝试将请求作为目录访问/index.html:如果前两者都失败,fallback 到首页
方案2:精确匹配路由文件
对于更精细的控制,可以为特定路由创建精确匹配规则。
server {
listen 80;
server_name your-domain.com;
root /path/to/your/nextjs/app;
# 首页
location = / {
try_files /index.html =404;
}
# 关于页面
location = /about {
try_files /about/index.html =404;
}
# 动态路由示例
location ~ ^/post/([^/]+)$ {
try_files /post/$1/index.html =404;
}
# 通用回退
location / {
try_files $uri $uri/ /index.html;
}
}方案3:使用 rewrite 规则
另一种方法是使用 rewrite 规则将请求重写到正确的文件路径。
server {
listen 80;
server_name your-domain.com;
root /path/to/your/nextjs/app;
# 重写规则:将所有非文件/非目录的请求重写到 index.html
if (!-e $request_filename) {
rewrite ^/(.*)$ /index.html last;
}
# 或者更精确的版本
location / {
if (!-f $request_filename) {
rewrite ^/(.*)$ /index.html break;
}
}
}最佳实践建议
- 优先使用方案1:try_files 指令是最简单且有效的解决方案
- 设置正确的根目录:确保 Nginx 的 root 指向 Next.js 导出的 out 目录
- 配置静态资源缓存:为 _next/static 目录设置长期缓存
- 启用 gzip 压缩:减少传输文件大小
- 测试所有路由:部署后务必测试各种路由场景
完整的生产环境配置示例
server {
listen 80;
server_name your-domain.com;
root /var/www/your-nextjs-app/out;
index index.html;
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
# 静态资源缓存
location /_next/static {
expires 365d;
access_log off;
add_header Cache-Control "public, immutable";
}
# 主路由配置
location / {
try_files $uri $uri/ /index.html;
}
# 错误处理
error_page 404 /404.html;
location = /404.html {
internal;
}
}验证解决方案
配置完成后,执行以下步骤验证:
- 重启 Nginx:
sudo nginx -s reload - 清除浏览器缓存或使用隐身模式测试
- 测试以下场景:
- 直接访问首页
- 直接访问子页面路由
- 使用浏览器前进/后退按钮
- 刷新页面
如果按照上述配置后问题仍然存在,请检查 Nginx 错误日志以获取更详细的错误信息。