You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用mod_rewrite映射URL至子目录PHP文件时遇无限循环问题求助

问题描述

我希望通过.htaccess中的mod_rewrite将"简洁URL"映射到子目录中对应的PHP文件,例如:
https://example.com/test/
映射到
https://example.com/p/test.php

我的现有规则步骤如下:

  1. 为所有缺少末尾斜杠的URL添加斜杠:
RewriteEngine on
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !(.*)/$
RewriteRule ^(.*[^/])$ $1/ [L,R=301]
  1. 添加规则确保请求的路径/文件本身不存在:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
  1. 最后添加RewriteRule将请求路径映射到子目录/p/中的对应文件:
RewriteRule ^(.+)$ /p/$1\.php

这套规则在请求存在的URL时正常工作,但请求不存在的URL时会触发无限循环,生成类似这样的URL:
https://example.com/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/p/test/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/.php/

我曾尝试在RewriteRule前添加另一条RewriteCond来确保子目录/p/中存在对应的文件,但该方法无效:

RewriteCond %{DOCUMENT_ROOT}/p/$1.php -f

请问我哪里操作出错了?

问题分析
  1. 无限循环的根源:当请求不存在的URL时,比如https://example.com/nonexistent/,规则会将其重写到/p/nonexistent.php,但这个文件同样不存在。此时Apache会再次执行mod_rewrite规则,重写后的路径/p/nonexistent.php会被第二条规则的!-f条件匹配,接着RewriteRule会把p/nonexistent.php作为(.+)捕获,重写成/p/p/nonexistent.php.php,以此类推形成无限循环。

  2. 文件存在判断失效的原因:你在RewriteCond中使用的$1,引用的是之前RewriteCond的捕获组,而非后续RewriteRule里的捕获组。这就导致%{DOCUMENT_ROOT}/p/$1.php中的$1不是你期望的请求路径部分,条件判断自然失效。

解决方案

修正规则,解决循环问题,同时正确判断目标文件是否存在:

RewriteEngine On
RewriteBase /

# 1. 给非文件类URL添加末尾斜杠
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*[^/])$ $1/ [L,R=301]

# 2. 仅当目标PHP文件存在时,映射简洁URL到/p/子目录
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
# 处理请求URI,去掉末尾斜杠后拼接.php,判断文件是否存在
RewriteCond %{DOCUMENT_ROOT}/p/%{REQUEST_URI}s.php -f
RewriteRule ^(.+)/$ /p/$1.php [L]

关键修正说明

  • 避免循环:RewriteRule的正则改为^(.+)/$,只匹配末尾带斜杠的原始请求,不会匹配重写后的/p/test.php这类路径;同时加上[L]标记,匹配后立即停止后续规则处理。
  • 正确判断文件存在:用%{REQUEST_URI}s处理请求路径——比如请求/test/时,%{REQUEST_URI}是/test/,加上s后会自动去掉末尾的斜杠变成/tests,拼接.php后就是/p/test.php,再结合%{DOCUMENT_ROOT}就能准确判断文件是否存在。

如果不想依赖%{REQUEST_URI}的字符串处理,也可以用捕获组引用的方式:

RewriteEngine On
RewriteBase /

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*[^/])$ $1/ [L,R=301]

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
# 捕获请求路径(去掉末尾斜杠)
RewriteCond ^(.+)/$ ^(.+)/$
RewriteCond %{DOCUMENT_ROOT}/p/%1.php -f
RewriteRule ^(.+)/$ /p/$1.php [L]

内容的提问来源于stack exchange,提问作者Alexander

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 14:39:56