一、为什么需要rewrite规则

在日常的Web开发中,我们经常会遇到需要修改URL结构的情况。比如网站改版后URL发生了变化,或者需要将动态URL转换为静态URL以便SEO优化。这时候就需要用到Nginx的rewrite功能了。

举个例子,假设我们有一个电商网站,原来的商品详情页URL是动态的:

http://example.com/product.php?id=123

现在我们希望改成更友好的静态URL:

http://example.com/product/123

这种场景下,rewrite规则就能大显身手了。它可以在不改变实际请求内容的情况下,修改用户看到的URL地址。

二、rewrite基础语法详解

Nginx的rewrite规则语法看起来简单,但实际使用时有很多需要注意的细节。我们先来看最基本的语法格式:

rewrite 正则表达式 替换目标 [flag];

这里的技术栈是Nginx配置语言。让我们通过一个完整的示例来说明:

server {
    listen 80;
    server_name example.com;
    
    # 将/product/123重写为/product.php?id=123
    rewrite ^/product/(\d+)$ /product.php?id=$1 last;
    
    # 将/about.html重定向到/about
    rewrite ^/about\.html$ /about permanent;
    
    location / {
        root /var/www/html;
        index index.php index.html;
    }
}

注释说明:

  1. 第一个rewrite规则匹配/product/后面跟着数字的URL,将其内部重写为product.php带参数的形式
  2. (\d+)捕获数字部分,通过$1引用
  3. last标志表示重写后继续处理其他规则
  4. permanent标志表示301永久重定向

三、rewrite的常见应用场景

3.1 动态URL转静态URL

这是rewrite最常用的场景之一。我们来看一个更复杂的例子:

rewrite ^/news/([0-9]{4})/([0-9]{2})/([0-9]{2})/([0-9]+)\.html$ /news.php?year=$1&month=$2&day=$3&id=$4 last;

这个规则将:

/news/2023/05/15/123.html

转换为:

/news.php?year=2023&month=05&day=15&id=123

3.2 域名重定向

当我们需要将多个域名统一到一个主域名时:

server {
    listen 80;
    server_name www.old-example.com old-example.com;
    
    # 将所有旧域名请求重定向到新域名
    rewrite ^(.*)$ http://www.new-example.com$1 permanent;
}

3.3 强制HTTPS

安全考虑,我们需要将所有HTTP请求重定向到HTTPS:

server {
    listen 80;
    server_name example.com;
    
    # 强制HTTPS
    rewrite ^(.*)$ https://$host$1 permanent;
}

四、rewrite的高级技巧

4.1 条件判断

Nginx的if指令可以和rewrite结合使用,实现更灵活的重定向:

# 根据用户设备重定向到不同页面
if ($http_user_agent ~* "(iPhone|Android)") {
    rewrite ^/(.*)$ /mobile/$1 break;
}

4.2 多级重写

有时候我们需要连续应用多个rewrite规则:

# 先去掉www前缀
rewrite ^(.*)://www\.example\.com(.*)$ $1://example.com$2 permanent;

# 然后添加语言前缀
rewrite ^/(en|fr|de)/(.*)$ /$2?lang=$1 last;

4.3 防止重定向循环

在使用rewrite时,特别要注意避免出现无限重定向的情况:

# 错误的例子:会导致循环
rewrite ^/(.*)$ /$1 permanent;

# 正确的做法:添加条件判断
if ($request_uri !~ "^/newpath/") {
    rewrite ^/(.*)$ /newpath/$1 permanent;
}

五、rewrite的性能优化

虽然rewrite很强大,但不当使用会影响性能。这里有几个优化建议:

  1. 尽量减少rewrite规则数量,合并相似规则
  2. 使用location优先匹配减少rewrite处理
  3. 避免在rewrite中使用复杂的正则表达式
  4. 合理使用flag控制处理流程
# 优化后的例子
location /products/ {
    # 只处理/products/下的请求
    rewrite ^/products/(\d+)$ /product.php?id=$1 last;
}

六、常见问题解决方案

6.1 重定向后丢失POST数据

使用307临时重定向可以保留POST数据:

rewrite ^/old-form$ /new-form redirect;

6.2 处理带查询参数的URL

# 保留原始查询参数
rewrite ^/search/(.*)$ /search.php?q=$1? last;

6.3 处理特殊字符

当URL中包含特殊字符时:

# 对中文等特殊字符进行编码处理
rewrite ^/search/(.*)$ /search.php?q=$1 last;

七、最佳实践总结

经过上面的讲解,我们可以总结出一些rewrite使用的最佳实践:

  1. 保持规则简单明了,添加详细注释
  2. 测试所有可能的边界情况
  3. 使用301永久重定向时要谨慎
  4. 监控rewrite规则的性能影响
  5. 定期审查和优化现有规则

最后分享一个综合性的配置示例:

server {
    listen 80;
    server_name example.com www.example.com;
    
    # 统一域名
    if ($host != 'example.com') {
        rewrite ^(.*)$ http://example.com$1 permanent;
    }
    
    # 静态资源直接处理
    location ~* \.(jpg|jpeg|gif|png|css|js)$ {
        expires 30d;
        access_log off;
    }
    
    # 商品页重写
    location /product/ {
        rewrite ^/product/(\d+)$ /product.php?id=$1 last;
    }
    
    # 新闻页重写
    location /news/ {
        rewrite ^/news/(\d+)/(\d+)/(\d+)/(\d+)\.html$ /news.php?year=$1&month=$2&day=$3&id=$4 last;
    }
    
    # 默认处理
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
}

希望通过这篇文章,你能全面掌握Nginx rewrite规则的使用技巧,在实际工作中灵活应用,解决各种URL重定向问题。