I have an MVC app with index.php located in a /public subfolder. Currently, I point Apache to the /public folder and use .htaccess in there to rewrite MVC requests to index.php.
Current Apache conf:
<VirtualHost>
DocumentRoot /var/www/html/public
</VirtualHost>
Current .htaccess in /public:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^(.*)$ index.php?$1 [L,QSA]
Target Apache conf:
<VirtualHost>
DocumentRoot /var/www/html
</VirtualHost>
Target .htaccess, but I can't get both the MVC and asset URLs to rewrite correctly:
DirectoryIndex index.php
RewriteEngine On
RewriteBase /public
# If it's a file/directory/symlink, serve it.
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d [OR]
RewriteCond %{REQUEST_FILENAME} -l
RewriteRule ^.*$ - [NC,L]
# Otherwise let's go MVC
RewriteRule ^.*$ index.php [NC,L]
Example URLs:
domain.com/login (MVC URL, rewritten to public/index.php)
domain.com/assets/styles.css (non-MVC URL, rewritten to public/assets folder)
CodePudding user response:
Unless you are setting up a local development environment to simulate a more restrictive live server environment then...
Not sure why you want to do this, as your current config (setting the
DocumentRoot
to/public
) would seem to be the most desirable (and simple) setup.Setting the
DocumentRoot
to the parent directory and rewriting to the/public
subdirectory instead is often a workaround when you are unable to change theDocumentRoot
. This is ultimately more work and exposes the/public
subdirectory, which now has to be blocked/redirected to prevent direct access.
Anyway, moving to a /public
subdirectory in the public HTML space...
Having set the DocumentRoot
to /var/www/html
there are two ways to accomplish this, with either 1 or 2 .htaccess
files. (Although, this also raises another question... why are you using .htaccess
when you have access to the server config? Although there can be perfectly valid reasons for this.)
Method#1 - Two .htaccess
files
It is arguably easier to do this with two .htaccess
files. One in the document root that rewrites all requests to the /public
subdirectory and one in the /public
subdirectory that rewrites requests to your MVC app, in much the same way you are doing already. The presence of the /public/.htaccess
file also serves to prevent a rewrite loop.
For example:
# (1) /.htaccess
RewriteEngine On
# (OPTIONAL) If you need to allow access to files/directories/symlinks
# outside of the "public" directory
#RewriteCond %{REQUEST_FILENAME} -f [OR]
#RewriteCond %{REQUEST_FILENAME} -d [OR]
#RewriteCond %{REQUEST_FILENAME} -l
#RewriteRule ^ - [L]
# Rewrite all requests to the "/public" subdirectory
RewriteRule (.*) public/$1 [L]
# (2) /public/.htaccess
RewriteEngine On
# If "public" directory is requested directly then redirect to remove it
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule (.*) /$1 [R=301,L]
# Stop early if already rewritten to MVC
RewriteRule ^index\.php$ - [L]
# Rewrite to MVC app, otherwise serve assets
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule (.*) index.php?$1 [QSA,L]
Method#2 - One .htaccess
file in the document root
Alternatively, you have just one .htaccess
file in the document root.
For example:
# /.htaccess (only)
RewriteEngine On
# If "public" directory is requested directly then redirect to remove it
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^public(?:/(.*))?$ /$1 [R=301,L]
# Stop early if already rewritten to "public" directory
RewriteRule ^public/ - [L]
# If it's a file/directory/symlink, serve it.
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d [OR]
RewriteCond %{REQUEST_FILENAME} -l
RewriteRule ^ - [L]
# Rewrite to assets in "public" directory if they exist
RewriteCond %{REQUEST_URI} \.\w{2,4}$
RewriteCond %{DOCUMENT_ROOT}/public%{REQUEST_URI} -f
RewriteRule !^public/ public%{REQUEST_URI} [L]
# Otherwise rewrite request to the MVC app in "public" directory
RewriteRule (.*) public/index.php?$1 [QSA,L]
I'm assuming all your "assets" have file extensions of between 2 and 4 characters. An optimisation to the above is if all your "assets" are referenced by /assets/...
then you could simply rewrite all these requests to /public/assets/...
- no need for filesystem checks. For example:
# Rewrite to assets in "public" directory (unconditionally)
RewriteRule ^assets/ public%{REQUEST_URI} [L]
Although this would mean you cannot have routes in the MVC app that start /assets/
. But you probably shouldn't anyway.
Aside: If you aren't serving directories directly (it's unusual to do so these days) then you don't need to check whether the request maps to a directory in the rewrite rules. Just allow it to pass to your MVC app and trigger a 404 (rather than a 403) - which could be preferable.