我有一个简单的网站,由 .php 文件组成,没有数据库。我使用 htaccess 来执行以下规则:
https://www.example.com/page.php
-> https://www.example.com/page
)最后一条规则,没有尾部斜杠,不起作用。相反,它会导致错误 404。这就是我要解决的问题。如果有人打开
https://www.example.com/page/
我希望它重定向到 https://www.example.com/page
而不是给 404.
这是我目前正在使用的相关
.htaccess
行。它基于 html5 样板 htaccess 添加了复制粘贴片段,因为我没有 .htaccess
知识。
ErrorDocument 404 /errors/404.php
Options -MultiViews
<IfModule mod_rewrite.c>
# (1)
RewriteEngine On
# (2)
Options +FollowSymlinks
</IfModule>
# to https
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R] # to 301
</IfModule>
# force www at beginning
<IfModule mod_rewrite.c>
RewriteEngine On
# # (1)
RewriteCond %{HTTPS} =on
RewriteRule ^ - [E=PROTO:https]
RewriteCond %{HTTPS} !=on
RewriteRule ^ - [E=PROTO:http]
# # (2)
# # RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteCond %{SERVER_ADDR} !=127.0.0.1
RewriteCond %{SERVER_ADDR} !=::1
RewriteRule ^ %{ENV:PROTO}://www.%{HTTP_HOST}%{REQUEST_URI} [L,R] # <- for test, for prod use [L,R=301]
</IfModule>
# remove .php
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^\.]+)$ $1.php [NC,L]
</IfModule>
# remove trailing /
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} (.*)/$
RewriteRule ^(.*)/$ $1 [L,R] # <- for test, for prod use [L,R=301]
</IfModule>
# remove .php <IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^([^\.]+)$ $1.php [NC,L] </IfModule> # remove trailing / <IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_URI} (.*)/$ RewriteRule ^(.*)/$ $1 [L,R] # <- for test, for prod use [L,R=301] </IfModule>
这些规则的顺序是错误的。
/page/
的请求首先被重写为 /page/.php
(这自然会导致 404)before 你删除尾部斜杠(它不再有尾部斜杠,因为它以 .php
结尾)。
但是,删除尾部斜线的规则应该是检查请求是notadirectory,而不是not文件。第二个检查
REQUEST_URI
的条件是多余的。您还缺少 substitution 字符串上的斜杠前缀(并且没有定义 RewriteBase
),因此这会导致格式错误的重定向。
但是,您的规则可以大大简化。不需要
<IfModule>
包装器或多个 RewriteEngine On
指令,非 www 到 www 重定向不必要地保留方案(HTTP 或 HTTPS),当它始终是 HTTPS 时。
你的规则可以这样写得更简洁:
ErrorDocument 404 /errors/404.php
Options +FollowSymLinks -MultiViews
RewriteEngine On
# to https
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# force www at beginning
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteCond %{SERVER_ADDR} !=127.0.0.1
RewriteCond %{SERVER_ADDR} !=::1
RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# remove trailing /
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)/$ /$1 [R=301,L]
# remove .php (Actually, this "appends" .php, it doesn't "remove" anything)
RewriteCond %{DOCUMENT_ROOT}/$1.php -f
RewriteRule ^([^.]+)$ $1.php [L]
我会质疑检查
SERVER_ADDR
的两个条件是否真的有必要,因为您在 HTTP 到 HTTPS 规则上没有类似的东西。
在正则表达式字符类中使用时无需反斜杠转义文字点。
您标记为“删除 .php”的规则实际上并未“删除”任何内容。它 appends 在
.php
扩展已经被删除的请求上的 .php
扩展。最好在尝试重写请求之前先测试相应的.php
文件是否存在,而不是无条件地附加.php
以希望该文件存在(这在某些情况下可能会导致意外错误,至少在 .php
请求上记录 404,而不是实际请求的 URL)。
如果请求 HTTP + 非 www,此规则块(根据您的原始规则块)也会导致两次重定向,因为您首先在同一主机上将 HTTP 重定向到 HTTPS。如果实施 HSTS,这实际上是一个要求,否则您可以通过颠倒前两个规则来避免这种双重重定向。 (但是,删除尾部斜杠的重定向也会导致编写的额外重定向。如果需要,可以更改此设置,但不会立即导致问题。)
NB:小心行尾注释(我已经删除了它们)。它们不受 Apache 支持。由于配置指令的解析方式,它们似乎可以正常工作,但如果您省略了任何可选参数,那么由于语法无效,您将收到 500 内部服务器错误。 (但是,是的,总是首先使用 302 - 临时 - 重定向进行测试。)