[英]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.我的问题是正确处理实际不存在的资源文件( js
、 css
或图像)。
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.当资源文件( js
、 css
或图像)不存在时,我当前的.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:它提供以下好处:
index
page is redirected to the public
directory.任何查看index
页面的尝试都会被重定向到public
目录。 [B] [乙]public
directory are caught and prevented from occurring.对public
目录的重写循环被捕获并防止发生。 [C] [C]public
directory. public
目录中允许直接访问资源文件(JS、CSS 等)。 [D] [D]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
文件中L
和END
之间的区别。 [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.