Apache mod_rewrite因URL未解码导致404,根本原因是AllowEncodedSlashes默认禁用且rewrite前不自动decode;需在主配置设AllowEncodedSlashes NoDecode,并配合RewriteRule的[B]标志防双编码。

Apache mod_rewrite 中 URL 未解码导致 404
PHP 脚本本身不拦截 URL 解码,真正出问题的是 Apache 的 mod_rewrite。当 URL 含 %2F(/)、%20(空格)、%E4%BD%A0(中文)等编码字符时,Apache 默认在匹配 RewriteRule 前**不会自动 decode**,而 $_GET 又在 PHP 层才解码——中间这层不一致,就导致重写规则根本匹配不到,直接 404。
- 典型现象:
https://example.com/search?q=hello%20world返回 404,但https://example.com/search?q=hello+world正常 - 关键判断:查看 Apache
error_log,出现类似File does not exist: /var/www/html/search,说明 rewrite 根本没生效 - 解决方向:强制 Apache 在匹配前解码 URL,用
B标志(escape backreferences)或NE+UNICODE配合AllowEncodedSlashes
启用 AllowEncodedSlashes 并设为 On 或 NoDecode
这是最直接有效的底层开关。默认 Apache 禁止含编码斜杠(%2F)的 URL 进入,连 rewrite 都不触发。必须显式放开:
AllowEncodedSlashes NoDecode
放在 httpd.conf 或虚拟主机配置中(不是 .htaccess)。注意:NoDecode 表示保留原始编码用于 rewrite 匹配,但最终传给 PHP 时仍会正确解码;若用 On,Apache 会提前 decode,可能导致路径穿越风险(如 %2E%2E),不推荐。
- 仅对含
%2F的 URL 必须设置,否则 404 不可避免 -
.htaccess中无法覆盖此指令,必须由管理员在主配置中开启 - Nginx 用户无需此步,它默认接受编码字符并正常转发
RewriteRule 加 B 标志避免双编码污染
当 rewrite 规则里用到了 $1、%{QUERY_STRING} 等变量拼接新 URL 时,若原 URL 已含 % 编码,Apache 默认会对这些变量内容再 encode 一次,造成 %2520(即 %20 被二次编码),PHP 接收后变成乱码或空值。
立即学习“PHP免费学习笔记(深入)”;
加 [B] 标志可让 Apache 自动 escape backreferences,避免重复编码:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?path=$1 [QSA,B,L]
- 没有
[B]时,访问/user/name%2Fid→path=user%252Fid(多了一层 %) - 加了
[B]后,path=user%2Fid,PHP 中$_GET['path']才能正确得到user/name - 搭配
QSA(Query String Append)才能保留原始查询参数中的编码字符
PHP 层检查 rawurldecode 是否必要
绝大多数情况不需要手动 decode —— PHP 的 $_GET、$_SERVER['REQUEST_URI'] 已自动处理标准 URL 编码(UTF-8)。但有两个例外:
- 当 Apache 配置了
AllowEncodedSlashes On(不推荐),且路径中含%2F,$_SERVER['PATH_INFO']可能保持原样,需rawurldecode($_SERVER['PATH_INFO']) -
前端用
encodeURIComponent编码了非标准字符(如 emoji、生僻字),而浏览器或代理做了额外转义,此时$_GET可能是 double-encoded,可用urldecode(urldecode($val))尝试修复 - 永远优先验证
$_SERVER['REQUEST_URI']原始值,比依赖$_GET更可靠
真正卡住的环节几乎都在 Apache 配置层,而不是 PHP 代码里补 decode。调错方向容易白忙活。











