为什么通过 HTACCESS 删除 .PHP 扩展后 301 重定向不起作用?

为什么通过 HTACCESS 删除 .PHP 扩展后 301 重定向不起作用?

我正在通过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 指令(目录语境)。

相关内容