I know this is a very popular question but I haven\'t been able to find a working solution for Laravel 5. I\'ve been trying to migrate from Codeigniter for a long time, but
After having installed Laravel for the first time, I faced the renowned "public folder problem" and I came up with this solution that, in my personal opinion, is "cleaner" then the others I found on the Web.
public
word in the URI.env
file against curious peopleEverything can be done just editing the .htaccess
using mod_rewrite
and four simple rules.
.htaccess
file in public/.htaccess
in the main rootI commented everything, so it should be clear (I hope) also to those who have never used mod_rewrite
(not that I'm an expert, all the opposite). Also, to understand the rules, it must be clear that, in Laravel, if Bill connects to https://example.com
, https://example.com/index.php
is loaded. This file just contains the command header("refresh: 5; https://example.com/public/")
, which sends the request to https://example.com/public/index.php
. This second index.php
is responsible to load the controller and other stuff.
# IfModule prevents the server error if the app is moved in an environment which doesn’t support mod_rewrite
Options -MultiViews
RewriteEngine On
# RULES ORIGINALLY IN public/.htaccess ---
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
# Handle Front Controller...
# RewriteCond %{REQUEST_FILENAME} !-d
# RewriteCond %{REQUEST_FILENAME} !-f
# RewriteRule ^ index.php [L]
# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# --- END
# PERSONAL RULES ---
# All the requests on port 80 are redirected on HTTPS
RewriteCond %{SERVER_PORT} ^80$
RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [R,L]
# When .env file is requested, server redirects to 404
RewriteRule ^\.env$ - [R=404,L,NC]
# If the REQUEST_URI is empty (means: http://example.com), it loads /public/index.php
# N.B.: REQUEST_URI is *never* actually empty, it contains a slash that must be set as match as below
# .* means: anything can go here at least 0 times (= accepts any sequence of characters, including an empty string)
RewriteCond %{REQUEST_URI} ^/$
RewriteRule ^(.*) /public/index.php [L]
# If the current request is asking for a REQUEST_FILENAME that:
# a) !== existent directory
# b) !== existent file
# => if URI !== css||js||images/whatever => server loads /public/index.php, which is responsible to load the app and the related controller
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule !^(css|js|images|media)/(.*)$ /public/index.php [L,NC]
# If the current request is asking for a REQUEST_FILENAME that:
# a) !== existent directory
# b) !== existent file
# => if URI == css||js||images[=$1]/whatever[=$2] => server loads the resource at public/$1/$2
# If R flag is added, the server not only loads the resource at public/$1/$2 but redirects to it
# e.g.: bamboo.jpg resides in example.com/public/media/bamboo.jpg
# Client asks for example.com/media/bamboo.jpg
# Without R flag: the URI remains example.com/media/bamboo.jpg and loads the image
# With R flag: the server redirects the client to example.com/public/media/bamboo.jpg and loads the image
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(css|js|images|media)/(.*)$ /public/$1/$2 [L,NC]
# --- END
The following rule (originally in public/.htaccess
) can be deleted. The same rule, in fact, is explicited in a more detailed way in the last two rules.
# Handle Front Controller...
# RewriteCond %{REQUEST_FILENAME} !-d
# RewriteCond %{REQUEST_FILENAME} !-f
# RewriteRule ^ index.php [L]
EDIT: I missed Abhinav Saraswat's solution and his answer should be the accepted one. Just one, simple and clear rule that redirects all the traffic to the public folder without modifying any file.