RewriteRule基础语法与匹配原理
RewriteRule是Apache mod_rewrite模块提供的核心指令,主要用于根据预设的规则对请求的URL进行重写或者重定向操作,是Apache实现伪静态、路由转发等功能的基础。该指令的完整语法格式为RewriteRule Pattern Substitution [flags],三个部分分别对应匹配模式、替换内容和可选的标志位,每个部分都有明确的生效逻辑,理解这些逻辑是正确配置规则的前提。
其中Pattern部分是基于Perl兼容正则表达式(PCRE)的匹配模式,用于匹配当前请求的URL路径部分(不包含域名、查询参数)。需要注意的是,匹配时默认是从URL的起始位置开始匹配,除非在正则中明确使用^和$限定边界,否则可能会匹配到路径中间的部分内容。例如规则RewriteRule article/(\d+) view.php?id=$1会匹配所有路径中包含article/数字的请求,而RewriteRule ^article/(\d+)$ view.php?id=$1只会匹配路径恰好为article/数字的请求,两者的匹配范围差异非常大。
Substitution部分是匹配成功后用来替换的原始URL地址,支持反向引用匹配到的正则分组,引用格式为$N(N为分组序号,从1开始)。如果替换内容以http://或者https://开头,那么规则会触发外部重定向,否则默认是服务器内部的URL重写,浏览器不会感知到URL的变化。比如下面的规则会将所有访问/blog/123的请求内部转发到/blog/view.php?id=123,用户浏览器地址栏依然显示/blog/123:
# 启用rewrite模块 LoadModule rewrite_module modules/mod_rewrite.so # 开启重写引擎 RewriteEngine On # 匹配blog/数字格式的请求,转发到view.php RewriteRule ^blog/(\d+)$ /blog/view.php?id=$1 [L]
flags部分是可选的标志位,用方括号包裹,多个标志位用逗号分隔,用来控制规则的行为。常用的标志位包括L(表示当前规则是最后一条规则,匹配后不再匹配后续规则)、R=301(触发301永久重定向)、R=302(触发302临时重定向)、NC(忽略大小写匹配)等。如果规则没有添加L标志,那么匹配当前规则后还会继续匹配后续的RewriteRule,很容易出现非预期的重写结果。
不同场景下的RewriteRule配置方案与选择
实际使用中需要根据需求选择不同的重写策略,核心区分点在于是需要内部重写还是外部重定向,以及是否需要保留原始的查询参数,不同的选择对应不同的配置方式,适配不同的业务场景。
如果需求是实现伪静态URL,也就是将动态URL转换为更友好的静态路径,同时不希望用户感知到真实的后端文件路径,那么应该选择内部重写,替换内容不需要加域名前缀,也不需要添加R标志。比如网站原来的商品详情页地址是/goods.php?cid=2&id=100,希望转换为/goods/2/100.html的静态路径,就可以使用如下配置:
RewriteEngine On # 将/goods/分类id/商品id.html重写为原动态地址 RewriteRule ^goods/(\d+)/(\d+)\.html$ /goods.php?cid=$1&id=$2 [L]
如果需求是旧域名跳转新域名、或者旧路径迁移到新路径,需要让用户浏览器更新地址栏的URL,那么就需要使用外部重定向,此时替换内容需要写完整的目标URL,或者添加R标志。如果是永久迁移,建议用301重定向,告诉搜索引擎旧地址的内容已经永久转移到新地址;如果是临时调整,使用302临时重定向即可。例如把所有来自old.ippipp.com的请求跳转到new.ippipp.com,配置如下:
RewriteEngine On
# 匹配旧域名的所有请求,跳转到新域名,301永久重定向
RewriteCond %{HTTP_HOST} ^old\.example\.com$ [NC]
RewriteRule ^(.*)$ https://new.ippipp.com/$1 [R=301,L]
这里用到了RewriteCond指令,它是RewriteRule的条件判断指令,只有当条件满足时才会执行后面的RewriteRule。上面的配置中,%{HTTP_HOST}是Apache的内置变量,代表当前请求的域名,只有域名匹配old.ippipp.com的时候才会触发跳转规则。如果需要保留原始请求的查询参数,默认情况下内部重写会自动保留原始查询参数,而外部重定向如果需要保留,需要手动在替换地址后加上%{QUERY_STRING}变量,或者使用QSA标志位,比如:
RewriteEngine On # 跳转时保留原始查询参数 RewriteRule ^old-page\.html$ /new-page.html [R=301,QSA,L]
添加QSA标志后,如果用户访问/old-page.html?ref=home,跳转到新地址时会自动变成/new-page.html?ref=home,不需要手动拼接查询参数。
RewriteRule配置的避坑注意事项
在实际配置RewriteRule的过程中,很多开发者会因为忽略细节导致规则不生效或者出现逻辑错误,以下是一些常见的坑点和对应的解决方案。
第一个常见问题是规则循环匹配导致500内部服务器错误。比如想要把所有请求都跳转到维护页面,错误地写了RewriteRule ^(.*)$ /maintain.html [R=302,L],此时访问维护页面本身也会匹配这条规则,再次跳转到维护页面,形成无限循环,最终服务器返回500错误。要解决这个问题,需要在规则前添加条件,排除维护页面本身的请求,配置如下:
RewriteEngine On
# 排除维护页面本身,避免循环跳转
RewriteCond %{REQUEST_URI} !^/maintain\.html$
RewriteRule ^(.*)$ /maintain.html [R=302,L]
第二个常见问题是正则匹配边界不明确导致规则覆盖非预期请求。比如想要匹配/user/123这种用户主页路径,写了RewriteRule user/(\d+) /user.php?id=$1 [L],那么像/user/123/order、/user/123/edit这种路径也会被匹配到,转发到/user.php?id=123,导致子路径无法访问。此时需要加上边界限定符,改成RewriteRule ^user/(\d+)$ /user.php?id=$1 [L],确保只匹配路径恰好为/user/数字的请求。
第三个常见问题是mod_rewrite模块没有启用导致规则完全不生效。很多新手直接写RewriteRule规则,却忘了先启用模块和开启重写引擎。在Apache的主配置文件httpd.conf中,需要找到#LoadModule rewrite_module modules/mod_rewrite.so这一行,去掉前面的#注释,然后重启Apache服务。如果是使用虚拟主机配置,还需要确保虚拟主机配置中允许使用重写规则,也就是AllowOverride指令设置为All,否则在.htaccess文件中写的规则不会生效,配置示例:
<VirtualHost *:80>
ServerName www.ipipp.com
DocumentRoot /var/www/html
# 允许.htaccess中的重写规则生效
<Directory /var/www/html>
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
第四个需要注意的点是标志位的顺序和组合逻辑,比如L标志和R标志一起使用时,只要匹配到规则就会直接重定向,不会再匹配后续规则,所以如果有多个规则,需要根据优先级从高到低排列,把更具体的规则放在前面,更通用的规则放在后面,避免前面的通用规则覆盖了后面的特殊规则。例如网站有管理后台路径/admin需要跳过重写,普通页面需要重写,那么管理后台的规则要放在前面:
RewriteEngine On # 管理后台路径不重写,直接访问 RewriteRule ^admin/ - [L] # 普通页面重写规则 RewriteRule ^page/(\d+)$ /page.php?id=$1 [L]
这里的-表示替换内容为空,也就是不做任何重写,直接匹配后结束规则,这样/admin下的所有请求都不会被后面的规则处理。

ApacheRewriteRuleURL重写修改时间:2026-08-29 05:54:29