Background
I am building a PHP
application that uses MVC principles. I am not every experienced in writing .htaccess
files, but I have a solution that works very well for my application. 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
.
My issue is properly handling resource files (js
, css
, or images) that do not actually exist.
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.
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? I'm referring to the last 3 rewrite conditions and 1 rewrite rule. These are crucial to my app.
htaccess code
# 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>
arkascha's answer helped point me in the right direction; using END
instead of L
in the correct locations. For those looking to create a similar security focused MVC structure my working .htacces
file is below. It provides the following benefits:
index
page is redirected to the public
directory. [B]public
directory are caught and prevented from occurring. [C]public
directory. [D]404
instead of a 500
. Thanks again to @arkascha for pointing out the difference between L
and END
in htacces
files. [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>