Nginx 重定向配置指南

目录

Nginx 是目前最流行的 Web 服务器之一,重定向配置是运维工作中的常见需求。但很多人对 returnrewrite 的区别不太清楚,导致配置效率低下甚至出错。

这篇文章会详细讲解 Nginx 重定向的各种配置方法,包括实战案例和踩过的坑。

return vs rewrite 的区别

说白了,returnrewrite 都能实现重定向,但它们的工作方式完全不同。

return 指令(推荐)

# 简单直接,性能最好
return 301 https://example.com;

特点:

rewrite 指令(功能强大但慢)

# 支持正则匹配和 URL 改写
rewrite ^/old-path/(.*)$ /new-path/$1 permanent;

特点:

💡 选择建议

能用 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 指令不是真正的条件判断,它会改变配置的执行顺序,可能导致意外行为。能避免就避免,实在要用也只在 returnrewrite 里用。

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

前面说过,returnrewrite 快得多。能用 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. 测试配置性能

修改配置后,用 abwrk 测试一下性能:

# 测试重定向性能
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 重定向配置的核心要点:

配置完成后,可以用我们的 免费重定向检测工具 验证跳转是否正常。