我正在通过Apache/2.2.26 (Unix)从我的网站 ( ).php
上特定目录下包含的文件中删除扩展名。courses
.htaccess
此外,我想将旧.php
版本 301 重定向到非 php 版本。
旧 URL 结构:
http://www.example.com/courses/blue-course.php
新的 URL 结构:
http://www.example.com/courses/blue-course
我目前的问题:
- 非
.php
版本的页面没有 301 重定向。 - 页面的两个版本
.php
和非版本均可查看。.php
这是我的代码:
RewriteEngine On
# External Routing
RewriteCond %{REQUEST_URI} (.*\/courses\/)([^\s]+)\.php [NC]
RewriteRule (.*courses\/)([^\s]+)\.php http://www.example.com/$1$2 [L,R=301]
# Internal Routing
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^ %{REQUEST_URI}.php [L]
答案1
我希望您的代码创建一个重定向循环,而不是“根本不进行 301 重定向”。因为较早的重定向还将捕获重写的 URL(由较后的指令)并重定向等。
非
.php
版本的页面没有 301 重定向。
难道不是吗?还是应该把这里的“非”或“不是”去掉?
页面的两个版本
.php
和非版本均可查看。.php
这似乎表明 MultiViews 可能已启用。需要禁用 MultiViews 才能正确执行 mod_rewrite 指令。(您可以使用 MultiViews或者mod_rewrite,但是,你可能会在尝试重定向如果启用了 MultiViews。)要确保 MultiViews 被禁用,请在文件顶部添加以下内容.htaccess
:
Options -MultiViews
但是,为了避免重定向循环,不要匹配REQUEST_URI
,因为 会随着 URL 重写而改变(与 匹配的 URL 路径相同)。RewriteRule
图案),您应该进行匹配THE_REQUEST
(它保存了请求的第一行并且不会改变)或者只是检查REDIRECT_STATUS
环境变量是否为空,以确保您只检查初始请求而不是重写的请求。
例如:
# External Routing
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule (.*courses\/)([^\s]+)\.php http://www.example.com/$1$2 [L,R=301]
注意:始终测试使用 302(临时)重定向来避免缓存问题。
但是,您的正则表达式可以稍微清理一下。根据您的示例 URL,/courses
是第一个路径段,但是,您的正则表达式在 URL 路径中的任何位置都匹配“courses/”。并且您允许重定向任何带有路径信息的 URL - 这是故意的吗?您只需要一个反向引用;而不是两个。并且不需要在RewriteRule
图案。您不一定需要在代换字符串,除非您有多个域?
因此,这也许可以“简化”为:
# External Routing
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^(courses/[^\s]+)\.php$ /$1 [L,R=301]
# Internal Routing RewriteCond %{REQUEST_FILENAME}.php -f RewriteRule ^ %{REQUEST_URI}.php [L]
这可能适用于您正在测试的特定 URL,但是,此规则很容易中断(并且它不是专门针对子目录/courses
)。%{REQUEST_FILENAME}.php
不一定与相同%{REQUEST_URI}.php
。 因此,虽然条件可能成功,但您仍然可能会重写为无效的 URL,这可能导致无限循环 - 500 内部服务器错误。
例如,给定一个请求/courses/blue-course/foo
(基于您的示例),其中/courses
是文件系统上的目录,blue-course.php
是您要重写到的文件(没有子目录/blue-course
),并且/foo
只是被错误地添加到 URL 的内容(甚至可能是由外部第三方恶意添加的),那么这将导致 500 错误。因为%{REQUEST_FILENAME}.php
解析为<document-root>/courses/blue-course.php
(存在)但%{REQUEST_URI}.php
解析为/courses/blue-course/foo.php
(不存在)。然后重写过程重新开始,导致无限循环。
可以通过将规则调整为类似以下内容来解决:
# Internal Routing
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.php -f
RewriteRule ^courses/ %{REQUEST_URI}.php [L]
假设您没有多个文件扩展名,或者文件基名中没有点,例如。blue-course.abc.php
那么也可以通过确保请求的 URL 还没有文件扩展名来优化(以避免测试任何静态资源)。例如:
# Internal Routing
RewriteCond %{REQUEST_URI} !\.\w{2,4}$
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.php -f
RewriteRule ^courses/ %{REQUEST_URI}.php [L]
!
上的前缀条件模式否定了它的意义。
总之
Options -MultiViews
RewriteEngine On
# External Routing
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^(courses/[^\s]+)\.php$ /$1 [L,R=301]
# Internal Routing
RewriteCond %{REQUEST_URI} !\.\w{2,4}$
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.php -f
RewriteRule ^ %{REQUEST_URI}.php [L]
上述指令假设您正在使用.htaccess
站点文档根目录中的文件。但是,如果您只需要定位特定目录,并且需要从父配置继承的其他 mod_rewrite 指令很少,那么.htaccess
在该子目录中创建一个附加文件以保持配置独立会很有帮助。但是,指令需要稍作修改:
RewriteEngine On
# External Routing
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^([^\s]+)\.php$ /courses/$1 [L,R=301]
# Internal Routing
RewriteCond %{REQUEST_URI} !\.\w{2,4}$
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.php -f
RewriteRule ^ %{REQUEST_URI}.php [L]
请注意,这将(默认)完全覆盖父配置中的任何 mod_rewrite 指令(目录语境)。