.htaccess没有.php和没有尾随斜杠

wqsoz72f  于 2023-04-07  发布在  PHP
关注(0)|答案(1)|浏览(149)

我有一个由.php文件组成的简单网站,没有数据库。我使用htaccess来执行以下规则:

  • 强制HTTPS
  • 在开始时强制www
  • 删除.php扩展名(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>
0qx6xfy6

0qx6xfy61#

# 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)* 在 * 你删除尾部斜杠之前(它不再有尾部斜杠,因为它以.php结尾)。
然而,你的规则应该是检查请求是 not a directory,而不是 not a file。第二个检查REQUEST_URI的条件是多余的。你还缺少 substitution 字符串上的斜杠前缀(并且没有定义RewriteBase),所以这将导致错误的重定向。
但是,您的规则可以大大简化。不需要<IfModule> Package 器或多个RewriteEngine On指令,并且当它始终是HTTPS时,非www到www重定向不必要地保留了方案(HTTP或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规则上没有类似的东西。
在正则表达式字符类中使用时,不需要反斜杠转义文字点。
您标记为“remove.php”的规则实际上并没有“删除”任何东西。它在.php扩展名已经被删除的请求上 * 追加 * .php扩展名。在尝试重写请求之前,最好先测试相应的.php文件是否存在,而不是无条件地附加.php,希望该文件存在(这在某些情况下可能导致意外错误,并且至少在.php请求上记录404,而不是实际请求的URL)。
此规则块如果请求HTTP + non-www,也会导致两次重定向,因为您首先将HTTP重定向到同一主机上的HTTPS。如果实现HSTS,这实际上是一个要求,但否则您可以通过反转前两个规则来避免这种双重重定向。(但是,删除尾部斜杠的重定向也会导致写入的额外重定向。如果需要,可以更改此重定向,但不会立即导致问题。)
注意:小心行尾注解(我已经删除了它们)。Apache不支持它们。它们可能看起来只是因为配置指令的解析方式而起作用,但是如果你省略了任何可选参数,那么你会得到一个500内部服务器错误,因为语法无效。(但是是的,总是先用302 - temporary - redirects测试。)

更新:

一切都像它应该的那样工作,除了这个:example.com/nonexistingfile.php。这导致了一个404,但奇怪的是,不是我的自定义404(ErrorDocument 404 /errors/404.php),而是一个“服务器备份404”。只是一个纯文本“文件未找到”。对此有什么想法吗?
这可能与PHP在服务器上的安装方式有关(与上面的指令无关)。有可能您的服务器最终将所有.php请求代理到后端PHP引擎,基本上绕过了.htaccess/ErrorDocument指令。
作为一种解决方法,你可以尝试使用mod_rewrite强制404,用于任何包含.php扩展名的请求(因为我假设没有客户端应该直接请求.php文件)。
例如,尝试在RewriteEngine On指令之后立即添加以下内容(或在“to https”规则之后添加,以确保404响应始终通过HTTPS):

# Force any "direct" request to a ".php" file to return a 404
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule \.php$ - [R=404]

REDIRECT_STATUS env var的检查确保此规则仅适用于来自客户端的直接请求,而不是在服务器上内部重写的请求(根据最后一条规则)。

相关问题