Skip to content

NGINX 伪静态与路径重写指南

NGINX 的伪静态和路径重写功能是 Web 服务器配置中的重要组成部分,它能够美化 URL、隐藏真实文件路径、实现 SEO 友好的链接结构,以及处理各种重定向需求。

目录

什么是伪静态和路径重写

伪静态概念

伪静态是指将动态 URL 转换为静态 URL 的形式,但实际上仍然由动态程序处理。例如:

  • 动态 URL:/article.php?id=123
  • 伪静态 URL:/article/123.html

路径重写概念

路径重写是指将一个 URL 请求重写为另一个 URL,可以是内部重写(不改变浏览器地址栏)或外部重定向(改变浏览器地址栏)。

rewrite 指令详解

基本语法

nginx
rewrite regex replacement [flag];

参数说明

  • regex: 正则表达式,用于匹配请求的 URI
  • replacement: 重写后的 URI
  • flag: 可选标志,控制重写行为

常用标志

nginx
# last - 停止处理当前请求,重新开始匹配 location
rewrite ^/old/(.*)$ /new/$1 last;

# break - 停止处理当前请求,继续处理
rewrite ^/old/(.*)$ /new/$1 break;

# redirect - 返回 302 临时重定向
rewrite ^/old/(.*)$ /new/$1 redirect;

# permanent - 返回 301 永久重定向
rewrite ^/old/(.*)$ /new/$1 permanent;

正则表达式基础

nginx
# 常用正则表达式符号
^           # 字符串开始
$           # 字符串结束
.           # 任意字符
*           # 零次或多次
+           # 一次或多次
?           # 零次或一次
\d          # 数字
\w          # 字母、数字、下划线
()          # 分组捕获
[]          # 字符集
|           # 或

location 指令与重写

location 匹配规则

nginx
# 精确匹配
location = /exact {
    # 只匹配 /exact
}

# 前缀匹配(优先)
location ^~ /prefix {
    # 匹配以 /prefix 开头的 URL
}

# 正则匹配
location ~ \.php$ {
    # 匹配以 .php 结尾的 URL
}

# 正则匹配(不区分大小写)
location ~* \.(jpg|jpeg|png|gif)$ {
    # 匹配图片文件
}

# 普通前缀匹配
location /normal {
    # 匹配以 /normal 开头的 URL
}

location 优先级

  1. = 精确匹配
  2. ^~ 前缀匹配
  3. ~~* 正则匹配(按配置顺序)
  4. 普通前缀匹配

常见重写场景

1. 去除文件扩展名

nginx
# 去除 .html 扩展名
location / {
    rewrite ^/(.*)\.html$ /$1 last;
    try_files $uri $uri.html $uri/ =404;
}

# 去除 .php 扩展名
location / {
    rewrite ^/(.*)\.php$ /$1 last;
    try_files $uri $uri.php $uri/ =404;
}

2. 添加文件扩展名

nginx
# 自动添加 .html 扩展名
location / {
    try_files $uri $uri.html $uri/ =404;
}

# 自动添加 .php 扩展名
location / {
    try_files $uri $uri.php $uri/ =404;
}

3. 文章详情页重写

nginx
# WordPress 风格的文章 URL
location / {
    rewrite ^/article/(\d+)$ /article.php?id=$1 last;
    rewrite ^/article/(\d+)/(.*)$ /article.php?id=$1&slug=$2 last;
}

# 自定义文章 URL
location / {
    rewrite ^/post/([a-zA-Z0-9-]+)$ /post.php?slug=$1 last;
    rewrite ^/post/(\d{4})/(\d{2})/([a-zA-Z0-9-]+)$ /post.php?year=$1&month=$2&slug=$3 last;
}

4. 分类页面重写

nginx
# 分类页面
location / {
    rewrite ^/category/([a-zA-Z0-9-]+)$ /category.php?name=$1 last;
    rewrite ^/category/([a-zA-Z0-9-]+)/page/(\d+)$ /category.php?name=$1&page=$2 last;
}

# 标签页面
location / {
    rewrite ^/tag/([a-zA-Z0-9-]+)$ /tag.php?name=$1 last;
    rewrite ^/tag/([a-zA-Z0-9-]+)/page/(\d+)$ /tag.php?name=$1&page=$2 last;
}

5. 用户页面重写

nginx
# 用户个人页面
location / {
    rewrite ^/user/([a-zA-Z0-9_-]+)$ /user.php?username=$1 last;
    rewrite ^/user/([a-zA-Z0-9_-]+)/profile$ /user.php?username=$1&page=profile last;
    rewrite ^/user/([a-zA-Z0-9_-]+)/posts$ /user.php?username=$1&page=posts last;
}

6. 搜索页面重写

nginx
# 搜索页面
location / {
    rewrite ^/search/(.*)$ /search.php?q=$1 last;
    rewrite ^/search/(.*)/page/(\d+)$ /search.php?q=$1&page=$2 last;
}

7. 多语言支持

nginx
# 语言前缀重写
location / {
    rewrite ^/(en|zh|ja)/(.*)$ /$2?lang=$1 last;
    rewrite ^/(en|zh|ja)$ /?lang=$1 last;
}

# 语言子目录重写
location / {
    rewrite ^/en/(.*)$ /$1?lang=en last;
    rewrite ^/zh/(.*)$ /$1?lang=zh last;
    rewrite ^/ja/(.*)$ /$1?lang=ja last;
}

8. API 重写

nginx
# RESTful API 重写
location /api/ {
    rewrite ^/api/users/(\d+)$ /api/user.php?id=$1 last;
    rewrite ^/api/users/(\d+)/posts$ /api/user_posts.php?user_id=$1 last;
    rewrite ^/api/posts/(\d+)$ /api/post.php?id=$1 last;
    rewrite ^/api/posts/(\d+)/comments$ /api/post_comments.php?post_id=$1 last;
}

# 版本化 API
location /api/ {
    rewrite ^/api/v1/(.*)$ /api/v1/$1 last;
    rewrite ^/api/v2/(.*)$ /api/v2/$1 last;
}

9. 静态资源重写

nginx
# 版本化静态资源
location /static/ {
    rewrite ^/static/(\d+\.\d+\.\d+)/(.*)$ /static/$2 last;
}

# CDN 回源重写
location /cdn/ {
    rewrite ^/cdn/(.*)$ /static/$1 last;
}

10. 旧链接重定向

nginx
# 301 永久重定向
location / {
    rewrite ^/old-page$ /new-page permanent;
    rewrite ^/old-category/(.*)$ /new-category/$1 permanent;
}

# 302 临时重定向
location / {
    rewrite ^/temp-page$ /new-page redirect;
}

高级重写技巧

1. 条件重写

nginx
# 基于请求方法的重写
location / {
    if ($request_method = POST) {
        rewrite ^/api/(.*)$ /api/post/$1 last;
    }
    if ($request_method = GET) {
        rewrite ^/api/(.*)$ /api/get/$1 last;
    }
}

# 基于 User-Agent 的重写
location / {
    if ($http_user_agent ~* "Mobile|Android|iPhone") {
        rewrite ^/(.*)$ /mobile/$1 last;
    }
}

2. 参数处理

nginx
# 保留查询参数
location / {
    rewrite ^/article/(\d+)$ /article.php?id=$1&$args last;
}

# 添加默认参数
location / {
    rewrite ^/search$ /search.php?q=&page=1 last;
    rewrite ^/search/(.*)$ /search.php?q=$1&page=1 last;
}

3. 复杂正则表达式

nginx
# 多级目录重写
location / {
    rewrite ^/([a-z]{2})/([a-z]+)/(\d{4})/(\d{2})/([a-zA-Z0-9-]+)$ /article.php?lang=$1&category=$2&year=$3&month=$4&slug=$5 last;
}

# 可选参数重写
location / {
    rewrite ^/product/([a-zA-Z0-9-]+)(?:/(\d+))?$ /product.php?slug=$1&page=$2 last;
}

4. 内部重写与外部重定向

nginx
# 内部重写(不改变浏览器地址)
location / {
    rewrite ^/internal/(.*)$ /app/$1 last;
}

# 外部重定向(改变浏览器地址)
location / {
    rewrite ^/external/(.*)$ /new/$1 permanent;
}

5. 错误页面重写

nginx
# 自定义错误页面
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;

location = /404.html {
    internal;
    rewrite ^/404.html$ /error/404.php last;
}

location = /50x.html {
    internal;
    rewrite ^/50x.html$ /error/50x.php last;
}

性能优化

1. 重写规则优化

nginx
# 避免过多的重写规则
location / {
    # 使用 try_files 替代部分重写
    try_files $uri $uri.html $uri.php $uri/ =404;
}

# 使用 map 指令优化
map $uri $rewrite_uri {
    default "";
    ~^/article/(\d+)$ /article.php?id=$1;
    ~^/category/([a-zA-Z0-9-]+)$ /category.php?name=$1;
}

location / {
    if ($rewrite_uri != "") {
        rewrite ^ $rewrite_uri last;
    }
}

2. 缓存优化

nginx
# 静态资源缓存
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}

# 重写后的页面缓存
location ~ \.php$ {
    fastcgi_cache my_cache;
    fastcgi_cache_valid 200 1h;
    fastcgi_cache_key "$request_method$request_uri";
}

3. 日志优化

nginx
# 自定义日志格式
log_format rewrite_log '$remote_addr - $remote_user [$time_local] '
                       '"$request" $status $body_bytes_sent '
                       '"$http_referer" "$http_user_agent" '
                       'rewrite: "$uri" -> "$request_uri"';

# 重写规则日志
location / {
    rewrite ^/article/(\d+)$ /article.php?id=$1 last;
    access_log /var/log/nginx/rewrite.log rewrite_log;
}

故障排除

1. 常见错误

nginx
# 重写循环
location / {
    # 错误:可能导致循环
    rewrite ^/(.*)$ /$1 last;
    
    # 正确:添加条件避免循环
    rewrite ^/(.*)$ /$1 last;
    if ($uri != $request_uri) {
        return 500;
    }
}

2. 调试技巧

nginx
# 启用重写日志
rewrite_log on;
error_log /var/log/nginx/rewrite.log notice;

# 测试重写规则
location /test/ {
    rewrite ^/test/(.*)$ /debug.php?uri=$1 last;
}

3. 测试工具

bash
# 测试 NGINX 配置
nginx -t

# 重新加载配置
nginx -s reload

# 查看 NGINX 进程
ps aux | grep nginx

# 查看错误日志
tail -f /var/log/nginx/error.log

4. 性能监控

nginx
# 启用状态页面
location /nginx_status {
    stub_status on;
    access_log off;
    allow 127.0.0.1;
    deny all;
}

完整配置示例

WordPress 伪静态配置

nginx
server {
    listen 80;
    server_name example.com;
    root /var/www/html;
    index index.php index.html;

    # WordPress 伪静态规则
    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    # 文章页面
    location ~ ^/article/(\d+)$ {
        rewrite ^/article/(\d+)$ /index.php?p=$1 last;
    }

    # 分类页面
    location ~ ^/category/([a-zA-Z0-9-]+)$ {
        rewrite ^/category/([a-zA-Z0-9-]+)$ /index.php?category_name=$1 last;
    }

    # 标签页面
    location ~ ^/tag/([a-zA-Z0-9-]+)$ {
        rewrite ^/tag/([a-zA-Z0-9-]+)$ /index.php?tag=$1 last;
    }

    # PHP 处理
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
    }
}

Laravel 伪静态配置

nginx
server {
    listen 80;
    server_name example.com;
    root /var/www/html/public;
    index index.php index.html;

    # Laravel 伪静态规则
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    # API 路由
    location /api/ {
        try_files $uri $uri/ /index.php?$query_string;
    }

    # PHP 处理
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
    }
}

最佳实践

1. 规则顺序

  • 将最具体的规则放在前面
  • 将通用规则放在后面
  • 避免规则冲突

2. 性能考虑

  • 使用 try_files 替代简单的重写
  • 避免过多的正则表达式
  • 合理使用缓存

3. 安全性

  • 避免暴露内部文件路径
  • 限制重写规则的作用范围
  • 定期审查重写规则

4. 维护性

  • 添加注释说明规则用途
  • 使用有意义的变量名
  • 定期测试重写规则

更多资源