mod_setenvif是Apache用于根据请求特征动态设置环境变量的模块,适用于日志标记、访问控制和条件缓存等场景,需配合LogFormat或Require env使用,且须注意正则匹配规则与配置位置。

mod_setenvif 是什么,什么时候该用它
mod_setenvif 是 Apache 的一个内置模块,作用是在请求处理早期,根据请求头、URL、客户端 IP 等特征,动态设置环境变量(ENV 变量)。它不修改响应,也不重写 URL,只做“打标签”这件事——后续的 LogFormat、Require env、SetEnvIf 条件判断等都依赖这些标签。
常见使用场景包括:
- 对爬虫或特定 UA 的请求打上
is_bot标签,用于日志隔离或限流 - 根据
Referer匹配盗链来源,配合deny from env=bad_referer - 识别内部 IP 段请求,设置
internal_request用于条件式缓存策略
注意:它不支持正则后向引用(如 $1),也不能在 .htaccess 中设置新环境变量(仅能继承父配置中已定义的)。
SetEnvIf 指令的基本写法和匹配逻辑
SetEnvIf 是核心指令,语法为:SetEnvIf attribute regex variable[=value]。其中:
-
attribute可以是请求头名(如User-Agent)、Remote_Addr、Request_URI、Server_Name等 -
regex是 PCRE 正则(Apache 2.4+ 默认启用 PCRE,无需加^或$锚定,只要子串匹配即生效) -
variable是环境变量名;若省略=value,则默认设为1
例如:
SetEnvIf User-Agent "Baiduspider|YisouSpider" is_bot SetEnvIf Referer "^https?://(?!example\.com).*" bad_referer SetEnvIf Remote_Addr "^10\.0\.0\." internal_request
容易踩的坑:
- 大小写敏感:默认匹配区分大小写,需加
i标志(如User-Agent ".mobile."i) - 空格不能省:
SetEnvIf User-Agent "Googlebot" is_bot中引号内前后不能有多余空格,否则匹配失败 - 特殊字符未转义:
.在正则中表示任意字符,域名中的点必须写成.
如何把环境变量用在日志和访问控制里
环境变量设好后,必须显式调用才能生效。不是“设了就自动起作用”。
日志中使用:
- 在
LogFormat中用%{VARNAME}e引用,例如:LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %{is_bot}e" combined_bot - 然后
CustomLog logs/access_log combined_bot
访问控制中使用:
- 需搭配
mod_authz_core,先Require env is_bot,再用RequireAll/RequireAny组合 - 示例(拒绝所有标记为爬虫的请求):
<requireall> Require all granted Require not env is_bot </requireall>
注意:
-
Require env只对当前上下文(Directory/Location)有效,不能跨作用域继承 - 若在虚拟主机外全局设置环境变量,子目录中仍可读取,但
Require必须显式写在对应块内
为什么 SetEnvIf 不生效?几个高频排查点
最常遇到的是“明明匹配了却不设变量”,原因往往很具体:
- 模块未启用:
a2enmod setenvif(Debian/Ubuntu)或确认LoadModule setenvif_module modules/mod_setenvif.so已加载 - 指令位置错误:放在
Directory块里时,SetEnvIf仅对匹配该路径的请求生效;想全局生效,应写在VirtualHost或主配置顶层 - 请求头被前端代理剥离:比如 Nginx 作为反向代理时没透传
User-Agent,导致 Apache 收到的是空值 - 环境变量名含非法字符:只能是字母、数字、下划线;
is-bot会静默失败,应写作is_bot - Apache 版本差异:2.2 中
SetEnvIfNoCase是独立指令;2.4+ 统一用SetEnvIf+i标志即可
真正麻烦的地方在于:它没有调试输出机制。验证是否命中,最直接的办法是临时加一条日志:LogFormat "%{User-Agent}i %{is_bot}e" debuglog,然后看日志里第二列是不是出现 1。









