简体   繁体   English

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

[英]How to get .htaccess to return 404 instead of 500 for missing files

Background背景

I am building a PHP application that uses MVC principles.我正在构建一个使用 MVC 原则的PHP应用程序。 I am not every experienced in writing .htaccess files, but I have a solution that works very well for my application.我在编写.htaccess文件方面并不是每个人都有经验,但我有一个非常适合我的应用程序的解决方案。 All requests are routed to a public/ directory for security and only absolute resources in this directory can be accessed, for example: http://localhost/public/css/main.css .出于安全考虑,所有请求都被路由到public/目录,并且只能访问该目录中的绝对资源,例如: http://localhost/public/css/main.css

My issue is properly handling resource files ( js , css , or images) that do not actually exist.我的问题是正确处理实际不存在的资源文件( jscss或图像)。

Issue & Question问题和问题

My current .htaccess rules are causing an infinite loop and returning an http 500 status when a resource file ( js , css , or images) does not exists.当资源文件( jscss或图像)不存在时,我当前的.htaccess规则会导致无限循环并返回http 500状态。

I know I need to catch this file does not exists problem and return an http 404 status instead, but how do I do that without skipping the last 4 lines of my rules?我知道我需要捕获此file does not exists问题并返回http 404状态,但是如何在不跳过规则的最后 4 行的情况下做到这一点? I'm referring to the last 3 rewrite conditions and 1 rewrite rule.我指的是最后 3 个重写条件和 1 个重写规则。 These are crucial to my app.这些对我的应用程序至关重要。

htaccess code 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>

Wrong approach in your question.你的问题方法错误。 You assume that you have to "catch" the file not found situation and return a 404. But that is not the case.假设您必须“捕获”文件未找到的情况并返回 404。但事实并非如此。 Actually that is the default behavior of the apache http server so you'd have to do nothing for that.实际上,这是 apache http 服务器的默认行为,因此您无需为此做任何事情。 Your issue is that you implemented a rewriting loop for those requests, that is what you need to break.您的问题是您为这些请求实现了重写循环,这就是您需要打破的。 Then a 404 will get returned as a standard response.然后 404 将作为标准响应返回。

The actual issue is a classic one: your last rewrite rule rewrites to public/index.php .实际问题是一个经典问题:您的最后一个重写规则重写为public/index.php That target however is matched again by the pattern of the same rewrite rule.然而,该目标再次与相同重写规则的模式匹配。 So your next rewriting target looks like public/index.php?p=public/index.php&p=some-page .所以你的下一个重写目标看起来像public/index.php?p=public/index.php&p=some-page Each time the rule rewrites the rewriting restarts, since the target has been altered.每次规则重写时,重写都会重新开始,因为目标已被更改。 That behavior is documented.该行为已记录在案。

You have two options:你有两个选择:

Either you need to use the END flag instead of the L flag in that rule (to finally terminate the rewriting process for that request:您需要在该规则中使用END标志而不是L标志(最终终止该请求的重写过程:

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

Or you need to add an additional condition to break that loop:或者您需要添加一个额外的条件来打破该循环:

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

I would definitely recomment the first approach, assuming that there are no further rewriting steps interfering here.我肯定会推荐第一种方法,假设这里没有进一步的重写步骤干扰。

arkascha's answer helped point me in the right direction; arkascha 的回答帮助我指出了正确的方向; using END instead of L in the correct locations.在正确的位置使用END而不是L For those looking to create a similar security focused MVC structure my working .htacces file is below.对于那些希望创建类似的以安全为重点的 MVC 结构的人来说,我的工作.htacces文件如下所示。 It provides the following benefits:它提供以下好处:

  • Directory listing is disabled and common sensitive files are forbidden from direct viewing.目录列表被禁用,常见的敏感文件被禁止直接查看。 [A] [一个]
  • Any attempt to view an index page is redirected to the public directory.任何查看index页面的尝试都会被重定向到public目录。 [B] [乙]
  • Rewrite loops to the public directory are caught and prevented from occurring.public目录的重写循环被捕获并防止发生。 [C] [C]
  • Direct access to resource files (JS, CSS, etc.) are allowed in the public directory. public目录中允许直接访问资源文件(JS、CSS 等)。 [D] [D]
  • Missing resource files properly return a 404 instead of a 500 .缺少资源文件正确返回404而不是500 Thanks again to @arkascha for pointing out the difference between L and END in htacces files.再次感谢@arkascha指出htacces文件中LEND之间的区别。 [E] [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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM