
本文详解 apache `.htaccess` 中因 `rewriterule` 顺序与条件判断不当引发的无限重定向循环(10 次内部重定向超限),并提供安全、可扩展的 clean url 实现方案。
在使用 Apache 的 mod_rewrite 实现“干净 URL”(如 /single-portfolio/3/title-slug)时,一个常见却隐蔽的陷阱是:重写规则的执行顺序与文件存在性检查不匹配,最终触发 Request exceeded the limit of 10 internal redirects 错误,返回 HTTP 500。
你原始的 .htaccess 存在两个关键问题:
- 规则顺序错误:通用 .php 后缀补全规则(RewriteCond %{REQUEST_FILENAME}.php -f)位于 single-portfolio/... 专用路由之前,导致所有以 /single-portfolio/ 开头的请求先被错误地重写为 /single-portfolio/xxx.php,而该路径并不存在(真实目标是 single-portfolio.php?id=...),但因 L 标志强制重启重写引擎,又反复追加 .php → /single-portfolio/xxx.php.php → .php.php.php……形成死循环;
- 条件与动作不一致:RewriteCond %{REQUEST_FILENAME}.php -f 检查的是当前请求路径 + .php 是否存在(如 /single-portfolio/3/title.php),但实际要重写的却是 single-portfolio.php —— 这属于“用错误的文件路径做判断”,极易引发误匹配和循环。
✅ 正确做法是:先处理特定路由,再处理通用后缀补全,并确保条件检查的目标与重写目标严格一致。
以下是修复后的推荐配置(已优化、去冗余、增强健壮性):
RewriteEngine On
# 【1】外部重定向:移除 .php 后缀(用户访问 /page.php → 301 跳转至 /page)
RewriteCond %{THE_REQUEST} \s/([^.]+)\.php[\s?] [NC]
RewriteRule ^ /%1 [R=301,L]
# 【2】专用路由:优先匹配 single-portfolio 模式(避免被后续规则干扰)
RewriteRule ^single-portfolio/(\d+)/([\w-]+)$ single-portfolio.php?id=$1&title=$2 [L]
# 【3】通用后缀补全:仅当请求路径 + .php 对应真实文件时才内部重写
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.php -f
RewriteRule ^ %{REQUEST_URI}.php [L]? 关键改进说明:
- ✅ 顺序调整:single-portfolio 规则置于通用 .php 补全之前,确保其优先命中;
- ✅ 条件精准化:使用 %{DOCUMENT_ROOT}%{REQUEST_URI}.php 替代 %{REQUEST_FILENAME}.php,明确基于文档根目录拼接,避免因别名或符号链接导致路径解析偏差;
- ✅ 移除冗余标志:NC(忽略大小写)在路由中非必要,反而可能造成 SEO 重复内容(如 /Single-Portfolio/ 和 /single-portfolio/ 被视为不同 URL);R 默认为 302,调试完成后建议改为 R=301 实现永久重定向;
- ✅ 正则精简:\d 替代 [0-9],\w 覆盖字母、数字与下划线(符合 slug 常见规范),- 单独保留以支持连字符。
⚠️ 注意事项:
- 修改后务必清空浏览器缓存(尤其是 301 重定向),并使用 curl -I 验证跳转行为;
- 生产环境启用前,在开发环境开启 LogLevel alert rewrite:trace3 查看重写过程(需管理员权限);
- 若网站部署在子目录(如 https://example.com/myapp/),需添加 RewriteBase /myapp/;
- 所有 PHP 入口文件(如 single-portfolio.php)必须真实存在且具有可执行权限,否则将返回 404。
总结:Clean URL 不是简单堆砌 RewriteRule,而是需要理解 Apache 重写引擎的单次匹配 + 循环重启机制。始终遵循「特例优先、条件与动作一致、路径绝对化」三原则,即可规避 500 错误,构建稳定、可维护的 URL 重写体系。










