Nginx 重定向配置指南
Nginx 是目前最流行的 Web 服务器之一,重定向配置是运维工作中的常见需求。但很多人对 return 和 rewrite 的区别不太清楚,导致配置效率低下甚至出错。
这篇文章会详细讲解 Nginx 重定向的各种配置方法,包括实战案例和踩过的坑。
return vs rewrite 的区别
说白了,return 和 rewrite 都能实现重定向,但它们的工作方式完全不同。
return 指令(推荐)
# 简单直接,性能最好
return 301 https://example.com;
特点:
- ✅ 速度快,直接返回 HTTP 响应
- ✅ 语法简单,不容易出错
- ✅ 不需要正则引擎,资源消耗少
- ✅ 官方推荐的方式
- ❌ 不支持复杂的 URL 改写
rewrite 指令(功能强大但慢)
# 支持正则匹配和 URL 改写
rewrite ^/old-path/(.*)$ /new-path/$1 permanent;
特点:
- ✅ 支持正则表达式
- ✅ 可以捕获和重用 URL 部分
- ✅ 功能强大,适合复杂场景
- ❌ 性能比 return 差
- ❌ 语法复杂,容易写错
- ❌ 可能导致死循环
💡 选择建议
能用 return 就不要用 rewrite。只有在需要正则匹配或复杂 URL 改写时才用 rewrite。
基础配置示例
301 永久重定向
server {
listen 80;
server_name old-domain.com;
# 整站重定向到新域名
return 301 https://new-domain.com$request_uri;
}
$request_uri 会保留原始的路径和查询参数,比如 /page?id=123。
302 临时重定向
server {
listen 80;
server_name example.com;
location /maintenance {
# 临时跳转到维护页面
return 302 /maintenance.html;
}
}
单个页面重定向
server {
listen 80;
server_name example.com;
# 旧页面跳转到新页面
location = /old-page.html {
return 301 /new-page.html;
}
}
注意 location = 是精确匹配,只匹配这个路径。
HTTP 转 HTTPS 跳转
这是最常见的需求,强制所有 HTTP 请求跳转到 HTTPS。
方法 1:单独的 HTTP server 块(推荐)
# HTTP 服务器,只做重定向
server {
listen 80;
server_name example.com www.example.com;
# 全部跳转到 HTTPS
return 301 https://$host$request_uri;
}
# HTTPS 服务器,实际处理请求
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# 你的网站配置...
}
这种方式最清晰,HTTP 和 HTTPS 分开配置,不会混乱。
方法 2:在 location 里判断(不推荐)
server {
listen 80;
listen 443 ssl http2;
server_name example.com;
# 判断协议,不是 HTTPS 就跳转
if ($scheme != "https") {
return 301 https://$host$request_uri;
}
# 其他配置...
}
这种方式能用,但不够优雅。而且 Nginx 社区有句名言:"if is evil"(if 指令是邪恶的),因为它在某些情况下会导致奇怪的问题。
⚠️ 关于 if 指令
Nginx 的 if 指令不是真正的条件判断,它会改变配置的执行顺序,可能导致意外行为。能避免就避免,实在要用也只在 return 或 rewrite 里用。
www 和非 www 统一
SEO 最佳实践是统一使用带 www 或不带 www 的域名,不要两个都能访问。
非 www 跳转到 www
server {
listen 80;
listen 443 ssl http2;
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# 跳转到 www 版本
return 301 https://www.example.com$request_uri;
}
server {
listen 80;
listen 443 ssl http2;
server_name www.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# 实际的网站配置...
}
www 跳转到非 www
server {
listen 80;
listen 443 ssl http2;
server_name www.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# 跳转到非 www 版本
return 301 https://example.com$request_uri;
}
server {
listen 80;
listen 443 ssl http2;
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# 实际的网站配置...
}
路径重定向
目录重定向
server {
listen 80;
server_name example.com;
# /blog/ 下的所有内容跳转到新位置
location /blog/ {
return 301 /articles$request_uri;
}
}
这样 /blog/post-1 会跳转到 /articles/blog/post-1。如果你想去掉 /blog/ 前缀,需要用 rewrite:
location /blog/ {
rewrite ^/blog/(.*)$ /articles/$1 permanent;
}
批量重定向
server {
listen 80;
server_name example.com;
# 多个旧路径跳转到新路径
location = /old-page-1.html { return 301 /new-page-1; }
location = /old-page-2.html { return 301 /new-page-2; }
location = /old-page-3.html { return 301 /new-page-3; }
}
如果重定向规则很多,可以用 map 指令:
map $uri $new_uri {
/old-page-1.html /new-page-1;
/old-page-2.html /new-page-2;
/old-page-3.html /new-page-3;
# 可以写几百条...
}
server {
listen 80;
server_name example.com;
if ($new_uri) {
return 301 $new_uri;
}
}
这种方式性能更好,因为 map 是在配置加载时处理的,不是每次请求都判断。
正则匹配重定向
URL 参数改写
server {
listen 80;
server_name example.com;
# 把 /product.php?id=123 改成 /product/123
location /product.php {
if ($args ~* "id=(\d+)") {
return 301 /product/$1;
}
}
}
文件扩展名改写
server {
listen 80;
server_name example.com;
# 把 .html 结尾的 URL 去掉扩展名
location ~ ^(.+)\.html$ {
return 301 $1;
}
}
这样 /page.html 会跳转到 /page。
多级路径改写
server {
listen 80;
server_name example.com;
# /category/subcategory/item 改成 /item
location ~ ^/category/[^/]+/(.+)$ {
return 301 /$1;
}
}
日期格式 URL 改写
server {
listen 80;
server_name example.com;
# /2025/02/13/post-title 改成 /post-title
location ~ ^/\d{4}/\d{2}/\d{2}/(.+)$ {
return 301 /$1;
}
}
常见坑和解决方案
1. rewrite 死循环
# ❌ 错误:会无限循环
location /old/ {
rewrite ^/old/(.*)$ /new/$1;
}
# 请求 /old/page 会变成 /new/page
# 但 /new/page 还是会匹配 location /old/,又变成 /new/new/page
# 然后 /new/new/page 又匹配...无限循环!
解决方法:
# ✅ 方法 1:用 permanent 或 redirect 标志
location /old/ {
rewrite ^/old/(.*)$ /new/$1 permanent;
}
# ✅ 方法 2:用 return(更好)
location /old/ {
return 301 /new$request_uri;
}
# ✅ 方法 3:用 last 标志并确保不会再次匹配
location /old/ {
rewrite ^/old/(.*)$ /new/$1 last;
}
location /new/ {
# 实际处理...
}
2. if 指令的坑
# ❌ 错误:if 里的 try_files 不会生效
location / {
if ($request_uri ~ ^/special/) {
try_files $uri $uri/ =404; # 这行不会执行!
}
}
# ✅ 正确:用 location 匹配
location ^~ /special/ {
try_files $uri $uri/ =404;
}
3. 查询参数丢失
# ❌ 错误:查询参数会丢失
location /old {
return 301 /new;
}
# /old?id=123 跳转后变成 /new,参数丢了
# ✅ 正确:保留查询参数
location /old {
return 301 /new$is_args$args;
}
# 或者更简单
location /old {
return 301 /new$request_uri;
}
4. 重定向优先级问题
# ❌ 错误:精确匹配会被忽略
location / {
return 301 https://example.com;
}
location = /special {
return 200 "This is special";
}
# /special 还是会跳转,因为 location / 先匹配了
# ✅ 正确:调整顺序或用 ^~
location ^~ /special {
return 200 "This is special";
}
location / {
return 301 https://example.com;
}
5. SSL 证书问题
# ❌ 错误:HTTPS 重定向但没配置证书
server {
listen 80;
server_name example.com;
return 301 https://example.com$request_uri;
}
# 用户访问 HTTP 会跳转到 HTTPS,但 HTTPS 没配置,报错!
# ✅ 正确:确保 HTTPS server 块存在且配置了证书
server {
listen 80;
server_name example.com;
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# 其他配置...
}
6. 多域名配置混乱
# ❌ 错误:所有域名都跳转到同一个地址
server {
listen 80;
server_name domain1.com domain2.com domain3.com;
return 301 https://domain1.com$request_uri;
}
# domain2.com 和 domain3.com 的访问都会跳到 domain1.com
# ✅ 正确:用 $host 变量保持域名
server {
listen 80;
server_name domain1.com domain2.com domain3.com;
return 301 https://$host$request_uri;
}
性能优化建议
1. 优先使用 return
前面说过,return 比 rewrite 快得多。能用 return 就不要用 rewrite。
2. 减少重定向链
# ❌ 不好:两次跳转
# HTTP → HTTPS → www
server {
listen 80;
server_name example.com;
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl;
server_name example.com;
return 301 https://www.example.com$request_uri;
}
# ✅ 更好:一次跳转
server {
listen 80;
listen 443 ssl;
server_name example.com;
return 301 https://www.example.com$request_uri;
}
3. 使用 map 处理大量重定向
如果有几百条重定向规则,用 map 比写几百个 location 块性能好得多。
map $request_uri $redirect_uri {
include /etc/nginx/redirects.map;
}
server {
listen 80;
server_name example.com;
if ($redirect_uri) {
return 301 $redirect_uri;
}
}
然后在 /etc/nginx/redirects.map 文件里写:
/old-url-1 /new-url-1;
/old-url-2 /new-url-2;
# 几百条...
4. 避免不必要的正则匹配
# ❌ 慢:每次都要正则匹配
location ~ ^/page-\d+\.html$ {
return 301 /pages;
}
# ✅ 快:精确匹配或前缀匹配
location ^~ /page- {
return 301 /pages;
}
5. 测试配置性能
修改配置后,用 ab 或 wrk 测试一下性能:
# 测试重定向性能
ab -n 10000 -c 100 http://example.com/old-page
# 或者用 wrk
wrk -t4 -c100 -d30s http://example.com/old-page
配置文件示例
这是一个完整的生产环境配置示例,包含了常见的重定向需求:
# HTTP 跳转到 HTTPS
server {
listen 80;
server_name example.com www.example.com;
return 301 https://www.example.com$request_uri;
}
# 非 www 跳转到 www
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
return 301 https://www.example.com$request_uri;
}
# 主站点配置
server {
listen 443 ssl http2;
server_name www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
root /var/www/html;
index index.html;
# 旧路径重定向
location = /old-about.html {
return 301 /about;
}
location = /old-contact.html {
return 301 /contact;
}
# 目录重定向
location /blog/ {
return 301 /articles$request_uri;
}
# 其他配置...
}
总结
Nginx 重定向配置的核心要点:
- 优先使用
return,性能好、不容易出错 - 只在需要正则匹配时才用
rewrite - 避免使用
if指令,它会导致奇怪的问题 - HTTP 转 HTTPS 用单独的 server 块,不要混在一起
- 注意保留查询参数,用
$request_uri或$is_args$args - 大量重定向规则用
map指令 - 避免重定向链,一次跳转到位
- 修改配置后记得
nginx -t测试语法
配置完成后,可以用我们的 免费重定向检测工具 验证跳转是否正常。