强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Apache HTTP Server 完全指南 / mod_rewrite 详解

mod_rewrite 详解

mod_rewrite 是 Apache 中最强大、最灵活的模块之一,提供基于规则的 URL 重写引擎。

1. 基础概念

1.1 工作原理

请求 URL → 匹配规则 → 应用条件 → 执行重写 → 输出 URL

mod_rewrite 在请求处理的不同阶段都可以工作:

阶段说明使用场景
per-server (VirtualHost)服务器配置阶段虚拟主机级别重写
per-dir (.htaccess)目录配置阶段.htaccess 中的规则

1.2 启用 mod_rewrite

# Debian/Ubuntu
sudo a2enmod rewrite
sudo systemctl reload apache2

# CentOS/RHEL
# 确保 httpd.conf 中包含:
# LoadModule rewrite_module modules/mod_rewrite.so

1.3 基本语法

# 启用重写引擎
RewriteEngine On

# 基本重写规则
RewriteRule 模式 替换 [标志]

# 带条件的重写
RewriteCond 测试字符串 条件模式 [标志]
RewriteRule 模式 替换 [标志]

2. RewriteRule 详解

2.1 语法

RewriteRule Pattern Substitution [FLAGS]

模式(Pattern):正则表达式,匹配请求的 URL 路径(不含查询字符串)

替换(Substitution):目标 URL 或文件路径

标志(Flags):控制重写行为

2.2 基本示例

# 简单重定向
RewriteRule ^old-page\.html$ /new-page.html [R=301,L]

# URL 路径重写(内部)
RewriteRule ^products/([0-9]+)$ /product.php?id=$1 [L]

# 多参数传递
RewriteRule ^category/([a-z]+)/page/([0-9]+)$ /list.php?cat=$1&page=$2 [L,QSA]

# 条件重写 - 仅当文件不存在时
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?url=$1 [L,QSA]

2.3 正则表达式常用语法

符号含义示例
^开始^/api/ 匹配以 /api/ 开头
$结束\.html$ 匹配以 .html 结尾
.任意字符a.c 匹配 abc, adc
*零或多次ab*c 匹配 ac, abc, abbc
+一或多次ab+c 匹配 abc, abbc
?零或一次ab?c 匹配 ac, abc
()捕获组(.*) 捕获整个路径
[]字符类[a-z] 小写字母
[^]否定字符类[^/]+ 非斜杠字符
\d数字[0-9]
\w单词字符[a-zA-Z0-9_]
\s空白空格、制表符等

2.4 反向引用

# $N - 第 N 个捕获组 (1-9)
RewriteRule ^user/([a-z]+)/profile$ /users.php?name=$1 [L]

# 多个捕获组
RewriteRule ^(\d{4})/(\d{2})/(\d{2})/(.+)$ /post.php?year=$1&month=$2&day=$3&slug=$4 [L]

# %{N} - 在 RewriteCond 中的反向引用
RewriteCond %{HTTP_HOST} ^(.+)\.example\.com$
RewriteRule ^(.*)$ /sites/%1/$1 [L]

3. RewriteCond 详解

3.1 语法

RewriteCond TestString CondPattern [FLAGS]

3.2 测试字符串变量

变量含义
%{HTTP_HOST}请求的主机名
%{REQUEST_URI}请求的 URI
%{REQUEST_FILENAME}请求的文件路径
%{QUERY_STRING}查询字符串
%{HTTP_REFERER}来源页面
%{HTTP_USER_AGENT}用户代理
%{REMOTE_ADDR}客户端 IP
%{HTTPS}是否 HTTPS
%{SERVER_PORT}服务器端口
%{TIME_YEAR}当前年份
%{TIME_MON}当前月份
%{TIME_DAY}当前日期

3.3 条件模式

# 正则匹配
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]

# 词法比较
RewriteCond %{HTTP_HOST} =example.com

# 整数比较
RewriteCond %{SERVER_PORT} >8080
RewriteCond %{SERVER_PORT} <9000

# 文件测试
RewriteCond %{REQUEST_FILENAME} -f    # 文件存在
RewriteCond %{REQUEST_FILENAME} !-f   # 文件不存在
RewriteCond %{REQUEST_FILENAME} -d    # 目录存在
RewriteCond %{REQUEST_FILENAME} !-d   # 目录不存在
RewriteCond %{REQUEST_FILENAME} -l    # 符号链接
RewriteCond %{REQUEST_FILENAME} -s    # 文件有大小(非空)

# 环境变量
RewriteCond %{ENV:REDIRECT_STATUS} ^$

3.4 条件标志

标志含义
[NC]不区分大小写
[OR]逻辑或(默认为逻辑与)
[NC,OR]组合

3.5 条件组合示例

# AND 逻辑(默认)
RewriteCond %{HTTPS} off
RewriteCond %{HTTP_HOST} ^www\. [NC]
# 两个条件都满足才执行

# OR 逻辑
RewriteCond %{HTTP_HOST} ^example\.com$ [NC,OR]
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
# 任一条件满足即执行

# 复杂组合
RewriteCond %{REQUEST_URI} !^/admin [NC,OR]
RewriteCond %{REMOTE_ADDR} ^192\.168\.1\.
RewriteCond %{REQUEST_FILENAME} !-f
# (不是 admin 路径 OR IP 在内网) AND 文件不存在

4. 标志(Flags)详解

4.1 常用标志

标志含义说明
[L]Last停止处理后续规则
[R=301]Redirect永久重定向
[R=302]Redirect临时重定向(默认)
[F]Forbidden返回 403 禁止
[G]Gone返回 410 已删除
[NC]No Case不区分大小写
[QSA]Query String Append追加查询字符串
[QSD]Query String Discard删除查询字符串
[PT]Pass Through传递给下一个处理器
[NE]No Escape不转义特殊字符
[NS]No Subrequest不匹配子请求
[END]End终止所有重写(比 L 更强)

4.2 重定向标志

# 301 永久重定向(SEO 友好)
RewriteRule ^old-page\.html$ /new-page.html [R=301,L]

# 302 临时重定向
RewriteRule ^maintenance\.html$ /coming-soon.html [R=302,L]

# 303 See Other
RewriteRule ^submit$ /thank-you.html [R=303,L]

# 307 Temporary Redirect(保持请求方法)
RewriteRule ^temp$ /new-location [R=307,L]

# 308 Permanent Redirect(保持请求方法)
RewriteRule ^permanent$ /new-location [R=308,L]

4.3 查询字符串处理

# QSA - 追加查询字符串
RewriteRule ^search/(.+)$ /search.php?q=$1 [L,QSA]
# /search/apache?page=2 → /search.php?q=apache&page=2

# QSD - 删除查询字符串(Apache 2.4+)
RewriteRule ^page$ /new-page [L,QSD]
# /page?old=param → /new-page

# QSA + QSD 组合
RewriteRule ^page$ /new-page?new=param [L,QSA,QSD]
# /page?old=param → /new-page?new=param

4.4 环境变量标志

# 设置环境变量
RewriteRule ^(.+\.php)$ $1 [E=PHP_SCRIPT:1]

# 基于环境变量的条件
RewriteCond %{ENV:PHP_SCRIPT} 1
RewriteRule ^(.*)$ /handler.php [L]

# REDIRECT_ 前缀
# 重写后,环境变量会加上 REDIRECT_ 前缀
# 如 E=FOO:bar → REDIRECT_FOO=bar

5. 常见用法与示例

5.1 SEO 相关

# 强制 HTTPS
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

# 移除 www
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]

# 添加 www
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^(.*)$ https://www.%{HTTP_HOST}/$1 [R=301,L]

# 移除尾部斜杠
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [R=301,L]

# 添加尾部斜杠(目录请求)
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^(.+[^/])$ $1/ [R=301,L]

# 移除 .html 扩展名
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^([^\.]+)$ $1.html [NC,L]

# 添加 .html 扩展名
RewriteCond %{REQUEST_URI} !\.[a-zA-Z0-9]{1,5}$
RewriteRule ^([^/]+)/?$ $1.html [L]

5.2 URL 路由

# MVC 路由
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?route=$1 [QSA,L]

# RESTful API 路由
# /api/users/123 → /api/users.php?id=123
RewriteRule ^api/users/([0-9]+)$ /api/users.php?id=$1 [L]
RewriteRule ^api/users$ /api/users.php [L]
RewriteRule ^api/posts/([0-9]+)$ /api/posts.php?id=$1 [L]

# 多语言 URL
# /en/about → /pages/about.php?lang=en
# /zh/about → /pages/about.php?lang=zh
RewriteRule ^(en|zh)/(.+)$ /pages/$2.php?lang=$1 [L,QSA]

5.3 安全防护

# 防止目录遍历
RewriteCond %{REQUEST_URI} \.\./  [OR]
RewriteCond %{REQUEST_URI} \.\.\\
RewriteRule .* - [F]

# 阻止特定 User-Agent
RewriteCond %{HTTP_USER_AGENT} (crawl|bot|spider|scraper) [NC]
RewriteRule .* - [F]

# 阻止空 User-Agent
RewriteCond %{HTTP_USER_AGENT} ^-?$
RewriteRule .* - [F]

# 保护敏感文件
RewriteRule \.(env|git|htaccess|htpasswd|ini|log|sh|sql|bak)$ - [F,NC]

# 防止图片盗链
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^https://(www\.)?example\.com [NC]
RewriteRule \.(jpg|jpeg|png|gif|bmp|webp)$ - [F,NC]

# 限制文件上传目录执行 PHP
RewriteCond %{REQUEST_URI} /uploads/ [NC]
RewriteRule \.php$ - [F]

5.4 重定向管理

# 域名迁移
RewriteEngine On
RewriteCond %{HTTP_HOST} ^(www\.)?old-domain\.com$ [NC]
RewriteRule ^(.*)$ https://new-domain.com/$1 [R=301,L]

# 子域名到子目录
RewriteCond %{HTTP_HOST} ^blog\.example\.com$ [NC]
RewriteRule ^(.*)$ /blog/$1 [L]

# 旧 URL 批量重定向
RedirectMatch 301 ^/old-category/(.*)$ /new-category/$1
RedirectMatch 301 ^/products\.php\?id=([0-9]+)$ /products/$1

# 带查询参数的重定向
RewriteCond %{QUERY_STRING} ^id=([0-9]+)$
RewriteRule ^product\.php$ /products/%1? [R=301,L]

5.5 性能优化

# 早期退出 - 将常用规则放前面
RewriteRule ^css/(.*)$ /css/$1 [L]
RewriteRule ^js/(.*)$ /js/$1 [L]
RewriteRule ^images/(.*)$ /images/$1 [L]

# 跳过静态文件
RewriteCond %{REQUEST_URI} \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ [NC]
RewriteRule .* - [L]

# 使用 RewriteMap 提高性能
# httpd.conf 中定义
RewriteMap lc int:tolower
RewriteRule ^(.*)$ ${lc:$1} [L]

6. RewriteMap 高级用法

6.1 映射类型

# 内部函数映射
RewriteMap lc int:tolower
RewriteMap uc int:toupper
RewriteMap escape int:escape

# 文本文件映射
RewriteMap redirects txt:/etc/apache2/redirects.txt

# 随机映射
RewriteMap servers rnd:/etc/apache2/servers.txt

# DBM 映射(高性能)
RewriteMap redirects dbm:/etc/apache2/redirects.dbm

# 外部程序映射
RewriteMap lookup prg:/etc/apache2/lookup.pl

6.2 映射文件格式

# /etc/apache2/redirects.txt
# 格式: key value
old-page.html    /new-page.html
old-about.html   /about
/contact         /contact-us
/blog/post-1     /blog/new-post-1

# /etc/apache2/servers.txt
# 随机选择后端
backend server1.example.com|server2.example.com|server3.example.com

6.3 使用 RewriteMap

# 使用文本映射
RewriteMap redirects txt:/etc/apache2/redirects.txt
RewriteRule ^(.+)$ ${redirects:$1} [R=301,L]

# 使用大小写转换
RewriteMap lc int:tolower
RewriteRule ^(.*)$ ${lc:$1} [L]

# 使用随机后端
RewriteMap servers rnd:/etc/apache2/servers.txt
RewriteRule ^(.*)$ http://${servers:backend}/$1 [P,L]

7. 调试与故障排除

7.1 启用日志

# httpd.conf
LogLevel alert rewrite:trace3

# 日志级别:
# trace1 - 基本信息
# trace2 - 详细信息
# trace3 - 非常详细
# trace4 - 调试信息
# trace5 - 极其详细
# trace6 - 最详细
# trace7 - 超详细
# trace8 - 全部信息

7.2 常见问题

# 问题:规则不生效
# 解决:检查 RewriteEngine 是否启用
RewriteEngine On

# 问题:死循环
# 解决:使用 [L] 标志和条件检查
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^(.*)$ index.php?url=$1 [L]

# 问题:.htaccess 不生效
# 解决:检查 AllowOverride
<Directory "/var/www/html">
    AllowOverride All
</Directory>

# 问题:特殊字符转义
# 解决:使用 [NE] 标志
RewriteRule ^search/(.+)$ /search.php?q=$1 [NE,L]

7.3 测试工具

# 在线测试
# https://htaccess.madewithlove.com/
# https://regex101.com/

# 命令行测试
curl -I -L http://localhost/old-page.html
curl -v http://localhost/test 2>&1 | grep -i location

# Apache 配置检查
apachectl configtest
apachectl -t -D DUMP_MODULES | grep rewrite

8. 注意事项

  1. 性能影响:每个请求都会经过所有 RewriteRule,规则越多性能影响越大
  2. 规则顺序:规则按顺序执行,先匹配的先执行
  3. 301 vs 302:永久迁移用 301,临时用 302,SEO 效果不同
  4. 测试环境:在测试环境充分测试后再部署到生产
  5. 备份规则:重写规则复杂,修改前务必备份

9. 扩展阅读

10. 总结

mod_rewrite 是 Apache 中最强大的模块:

  • 灵活的 URL 重写:支持复杂的正则表达式匹配
  • 条件逻辑:RewriteCond 提供丰富的条件判断
  • 多种标志:精确控制重写行为
  • 广泛应用:SEO、安全、路由、性能优化

掌握 mod_rewrite 是 Apache 管理员的必备技能。