3.3 重写引擎mod_rewrite:改写请求的方向


文档摘要

3.3 重写引擎 modrewrite:改写请求的方向 本节摘要:modrewrite 是 httpd 里权柄最大也最易误用的模块——它能在请求找到内容之前改写其 URL(内部改写,用户无感),或直接命令浏览器改道(外部重定向)。本节讲清两种模式的天壤之别、规则的执行顺序与匹配语法,给出从"强制 HTTPS"到"优雅链接"的常用套路,并交付一张重写问题排查路线图。掌握它,你就掌握了请求旅程的信号灯。 你应当带走的能力 阅读完本节,你应当能够: 区分内部改写与外部重定向并选择正确的标志位; 解释规则执行顺序:条件先行、自上而下、循环保护; 写出强制 HTTPS、去 www、优雅链接三类高频规则; 用重写日志定位"规则不生效或误伤";

3.3 重写引擎 mod_rewrite:改写请求的方向

本节摘要:mod_rewrite 是 httpd 里权柄最大也最易误用的模块——它能在请求找到内容之前改写其 URL(内部改写,用户无感),或直接命令浏览器改道(外部重定向)。本节讲清两种模式的天壤之别、规则的执行顺序与匹配语法,给出从"强制 HTTPS"到"优雅链接"的常用套路,并交付一张重写问题排查路线图。掌握它,你就掌握了请求旅程的信号灯。

你应当带走的能力

阅读完本节,你应当能够:

  1. 区分内部改写与外部重定向并选择正确的标志位;
  2. 解释规则执行顺序:条件先行、自上而下、循环保护;
  3. 写出强制 HTTPS、去 www、优雅链接三类高频规则;
  4. 用重写日志定位"规则不生效或误伤";
  5. 判断哪些场景应该改用更简单的 Redirect 或 Location 匹配。

两种"改道",别混为一谈

重写引擎的第一课是分清两个动作:

内部改写:服务器把 URL 换成另一个内部路径再继续处理,浏览器全程不知情,地址栏不变。典型如内容管理系统的"优雅链接"——用户访问的是干净的语义 URL,服务器默默把它翻译成真正的入口脚本。

外部重定向:服务器返回 301/302 与新地址,命令浏览器重新发起一次全新的旅程。地址栏会变,SEO 权重随 301 迁移。

两者的区分体现在规则标志上:

# 内部改写:无 R 标志,服务器内部换路径 RewriteRule ^article/(\d+)$ article.php?id=$1 [L] # 外部重定向:R=301,浏览器收到"去新地址" RewriteRule ^article/(\d+)$ https://www.example.com/post/$1 [R=301,L]

选错动作的症状很有辨识度:本想改写却用了重定向,用户地址栏出现 article.php?id=7 的"内脏外露";本想重定向却只做了改写,搜索引擎两个地址都收录成重复内容。

引擎的运转方式:三个铁律

铁律一:条件与规则成对,条件只作用于紧随其后的那条规则。

RewriteCond %{HTTPS} off RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]

这两行的意思:当 HTTPS 是关闭状态时,把任意路径重定向到 https 版本。Cond 像开关,Rule 像动作;一个 Rule 前可以摞多个 Cond,默认全部满足才执行,加 [OR] 标志变为任一满足。

铁律二:规则集自上而下逐条尝试,命中即按标志行动。 [L](last)表示"本轮到此为止"——但注意它不是彻底终止:改写后 URL 会重新进入规则集再走一轮(这就是循环的来源),直到 URL 不再变化或循环次数上限触发 500 报错。

铁律三:作用域决定上下文。 写在虚拟主机级的规则处理的是原始 URL(此时还没映射到文件系统,规则里用 %{REQUEST_URI});写在 Directory 或 .htaccess 里的规则处理的是已映射的相对路径(不含开头斜杠,规则里用相对形式匹配)。同一个规则从虚拟主机搬进 .htaccess 通常要改写匹配模式——不理解这点,"换个地方就不生效"就是必然。

高频套路三则

套路一:全站强制 HTTPS。 就是上面的 Cond/Rule 组合,注意别漏 Cond——没有条件的强制规则会把 https 请求也重定向到 https,形成无限循环,直到触发 500。

套路二:统一规范域名(去 www 或加 www)。

RewriteCond %{HTTP_HOST} ^example\.com$ RewriteRule ^(.*)$ https://www.example.com/$1 [R=301,L]

SEO 的要点是固定一个规范形式,301 而非 302——302 不迁移权重。

套路三:优雅链接。 第一节的内部改写即是。升级版要挡住对入口脚本的直接访问,并把查询参数透传:

RewriteEngine On # 已是目标形式的请求直接放行,防止循环 RewriteRule ^article\.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.+)$ index.php?route=$1 [L]

倒数两行的 Cond 是"文件不存在且目录不存在才改写"——这组组合让静态文件、真实目录不被误吞进入口脚本,是所有内容管理系统的标准开场白。

⚠️ 常见坑:.* 开头的贪婪模式匹配一切,放在规则集顶部会"先到先得"挡住后面所有规则。规则顺序是语义的一部分,改规则先看它在上游会被谁截胡。

能用 Redirect 就别动重写引擎

重写引擎是"瑞士军刀里的电锯":能干一切,但代价是正则复杂度、规则顺序敏感、每请求的评估开销。三件事有更便宜的专用工具:

整前缀搬迁用 Redirect 指令(mod_alias):

Redirect permanent /docs https://docs.example.com

一行完成,无正则、无顺序问题、无循环风险。

单目录代理用 Location 加 ProxyPass(第 4 章展开),不用重写规则包代理。

"按文件存在性分发":优先用 DirectoryIndex 与 ErrorDocument 组合,或前端控制器的入口文件自身做路由——应用层路由成熟的今天,服务器层重写只该负责"入口收敛"这一小步。

工程判断标准一句话:规则数量在三条以内、且未来不打算增长,用重写没问题;一旦发现自己在服务器层写业务路由,就该把路由逻辑还给应用。

排查:让重写引擎摊开给你看

重写问题之所以难查,是因为它在默认日志里几乎沉默。打开专用追踪(2.4 用日志级别,老版本用 RewriteLog 指令):

# 在虚拟主机或全局日志配置里 LogLevel alert rewrite:trace3 # 然后实时观察(trace3 信息量适中,trace8 是洪水级别) tail -f error_log | grep rewrite

你会看到每条规则的匹配尝试、Cond 的真假判定、最终动作——"为什么这条规则没命中"的答案几乎都在这里。几个高频结论:模式少写了开头的锚定符;相对/绝对上下文用错;上游规则加了 L 把流量截走;Cond 测试的变量名拼错(变量名大小写敏感)。

图 3-3 重写问题排查路线

图 3-3 重写问题排查路线

本节要点回顾

  • 两种动作:内部改写用户无感,外部重定向浏览器重新出发,标志 R 区分;
  • 三条铁律:Cond 只管下一条 Rule、自上而下命中即动、作用域决定匹配的 URL 形态;
  • 三大套路:强制 HTTPS 要带 Cond、规范域名用 301、优雅链接配存在性条件;
  • 降级原则:整前缀搬迁用 Redirect,简单分发别用电锯;
  • 排查武器:rewrite:trace3 日志让引擎摊牌,顺序问题占不生效原因的大头;
  • 循环警报:500 且日志见反复重写,先找缺的那条"放行"规则。

💡 一句话记住本节:重写引擎是信号灯,不是赛车引擎——方向对了一切都顺,方向错了全是环岛。

至此旅程第五站的岔路讲完。下一章请求进入干活的车间:模块如何代理、如何生成动态内容。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U