如何让.htaccess对丢失的文件返回404而不是500

jxct1oxe  于 2022-11-16  发布在  其他
关注(0)|答案(2)|浏览(138)

背景

我正在构建一个使用MVC原则的PHP应用程序。我并不是很擅长编写.htaccess文件,但是我有一个非常适合我的应用程序的解决方案。为了安全起见,所有的请求都被路由到public/目录,并且只有这个目录中的绝对资源可以访问,例如:http://localhost/public/css/main.css .
我的问题是正确处理实际上不存在的资源文件(jscss或图像)。

问题与疑问

当资源文件(jscss或图像)不存在时,我当前的.htaccess规则会导致无限循环并返回http 500状态。
我知道我需要捕获这个file does not exists问题并返回一个http 404状态,但是我如何在不跳过我的规则的最后4行的情况下做到这一点呢?我指的是最后3个重写条件和1个重写规则。这些对我的应用程序至关重要。

htaccess代码

# Disable index view.
Options -Indexes

# Hide sensitive files.
<Files ~ "\.(env|json|config|md|gitignore|gitattributes|lock|yml|xml)$">
    Order allow,deny
    Deny from all
</Files>

# Redirect all requests to the public directory.
<IfModule mod_rewrite.c>
    RewriteEngine On
    # Rewrite base if site is not at the servers root.
    RewriteBase /sub/directory/
    # If nothing or an index page is requested go to `public/index.php` and stop processing.
    RewriteRule ^(\/||.*index\..*)$ public/index.php [L,QSA]
    # If the URL already contains the `public/` directory go to URL and stop processing.
    RewriteRule ^(.*public\/.*)$ $1 [L,QSA]
    # Does it appear that this URL is for a resource (JS, CSS, etc.)?
    # ▼ ▼ ▼ ISSUE STARTS HERE ▼ ▼ ▼
    RewriteCond %{REQUEST_URI} ^([\w\d\s\-\_\/\%]*)\.(?!php|phtml|htm|html).*$
    # Yes, prefix with `public/` and go to the modified URL.
    RewriteRule ^(.*)$ public/$1 [L,QSA]
    # Rewrite all remaining URLs to our apps MVC structure.
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-l
    RewriteRule ^([\w\d\s\-\_\/\%]*)$ public/index.php?p=$1 [L,QSA]
</IfModule>
yks3o0rb

yks3o0rb1#

你的问题方法不对。你 * 假设 * 你必须“捕捉”文件未找到的情况并返回404。但事实并非如此。实际上这是apache http服务器的默认行为,所以你不必为此做任何事情。你的问题是你为这些请求实现了一个重写循环。这就是你需要打破的。然后一个404将作为标准响应返回。
实际的问题是一个经典的问题:您的上一个重写规则重写为public/index.php。但是,该目标 * 再次 * 与同一重写规则的模式匹配。因此,您的下一个重写目标看起来像public/index.php?p=public/index.php&p=some-page。每次规则重写时,重写都会重新开始,因为目标已被更改。该行为已记录在案。
您有两个选项:
您需要在该规则中使用END标志而不是L标志(以 * 最终 * 终止该请求的重写过程:

RewriteRule ^([\w\d\s\-\_\/\%]*)$ public/index.php?p=$1 [END,QSA]

或者,您需要添加一个附加条件来中断该循环:

RewriteCond %{REQUEST_URI} !public/index\.php$

我肯定会重新评论第一种方法,假设这里没有进一步的重写步骤干扰。

svdrlsy4

svdrlsy42#

arkascha's answer帮助我指明了正确的方向;在正确的位置使用END而不是L。对于那些希望创建类似的以安全为中心的MVC结构的人,我的工作文件.htacces如下。它提供了以下好处:

  • 禁用目录列表,禁止直接查看常见的敏感文件。[A]
  • 任何查看index页面的尝试都将被重定向到public目录。[B]
  • 捕获并防止public目录的重写循环。[C]
  • 允许直接访问public目录中的资源文件(JS、CSS等)。[D]
  • 丢失的资源文件正确返回404而不是500。再次感谢@arkascha指出LENDhtacces文件中的区别。[E]
# Disable index view. [A]
Options -Indexes

# Hide sensitive files. [A]
<Files ~ "\.(env|json|config|md|gitignore|gitattributes|lock|yml|xml)$">
    Order allow,deny
    Deny from all
</Files>

# Redirect all requests to the public directory.
<IfModule mod_rewrite.c>
    RewriteEngine On
    # Rewrite base if site is not at the servers root.
    RewriteBase /sub/directory/
    # If nothing or an index page is requested go to `public/index.php` and stop processing. [B]
    RewriteRule ^(\/||.*index\..*)$ public/index.php [END,QSA]
    # If the URL already contains the `public/` directory go to URL and stop processing. [C][E]
    RewriteRule ^(.*public\/.*)$ $1 [END,QSA]
    # Does it appear that this URL is for a resource (JS, CSS, etc.)?
    RewriteCond %{REQUEST_URI} ^([\w\d\s\-\_\/\%]*)\.(?!php|phtml|htm|html).*$
    # Yes, prefix with `public/` and go to the modified URL. [D]
    RewriteRule ^(.*)$ public/$1 [L,QSA]
    # Rewrite all remaining URLs to our apps MVC structure. [E]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-l
    RewriteRule ^([\w\d\s\-\_\/\%]*)$ public/index.php?p=$1 [END,QSA]
</IfModule>

相关问题