6

I using this .htaccess code to direct all requests to Public directory:

    <IfModule mod_rewrite.c>
        RewriteEngine On

        RewriteRule ^(.*)$ public/$1 [L]
    </IfModule>

Site structure:

Public_html/
  app/
  public/
    app/
    css/
    index.php
  index.php

This code works fine but fails when I hit on existing directories in root:

  • www.example.com/css -> public_html/public/css - works ok
  • www.example.com/app -> public_html/app - not ok, must be public_html/public/app

How to ignore existing directories and rewrite everything to public dir

  • "This code works fine" - You must have something else... the code you have posted would result in a rewrite loop (500 Internal Server Error) for all requests? But likewise, the code you have posted would already "ignore existing directories and rewrite everything to public dir"? – MrWhite Aug 26 '16 at 15:20
5

This code works fine

You must have something else... the code you have posted would result in a rewrite loop (500 Internal Server Error) for all requests?

But neither do you have to do anything special to "ignore existing directories and rewrite everything to public dir". So again, there must be something else going on here?

However, what you do need to do in order to avoid a rewrite loop is to include a condition to avoid rewriting to the "public" subdirectory when you have already rewritten to the "public" subdirectory. For example:

RewriteCond %{REQUEST_URI} !^/public/
RewriteRule (.*) public/$1 [L]

Without the RewriteCond directive /something would get rewritten to /public/something to /public/public/something to /public/public/public/something, etc...

Incidentally, you don't need anchors (^ and $) if you are matching the entire string, so ^(.*)$ and (.*) are the same.

<IfModule mod_rewrite.c>

Aside: Unless your site is intended to "work" without mod_rewrite, you should remove the <IfModule...> wrapper.

UPDATE:

Comment: the existing .htaccess is from laravel developers:

<IfModule mod_rewrite.c>
    <IfModule mod_negotiation.c>
        Options -MultiViews
    </IfModule>

    RewriteEngine On

    # Redirect Trailing Slashes If Not A Folder...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)/$ /$1 [L,R=301]

    # Handle Front Controller...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]
</IfModule>

Ok, this makes more sense, but it's also part of the "problem". You can't just add another directive and expect it to work. You need to work with the existing directives (if that is the intention). The order of the mod_rewrite directives is important.

This is made more complex because you wish to ignore existing files and directories in the document root (as per your question). Also, the /public subdirectory presumably needs to be kept private, otherwise we could simply redirect. The "Redirect Trailing Slashes" then becomes a bit tricky as we can't redirect to the /public directory (since that obviously exposes the hidden directory), but we need to check the existence of directories in that directory, not the document root (in which we are currently located). You also need to block direct access to the /public directory (or rather redirect back to the root).

Try the following (just the inner, mod_rewrite directives are included after RewriteEngine):

# Prevent direct access to the "public" folder - redirect to root
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /public/
RewriteRule ^public/(.*) /$1 [R=302,L]

# Redirect Trailing Slashes If Not A Folder...
# - but look for the file in the "public" folder
#   (ensure we are not already in the "public" folder)
RewriteCond %{REQUEST_URI} !^/public/
RewriteCond %{DOCUMENT_ROOT}/public/$1 !-d
RewriteRule ^(.*)/$ /$1 [R=302,L]

# Rewrite "everything" to the "public" subdirectory if not already
# This ignores existing files/dirs in the document root
RewriteCond %{REQUEST_URI} ^/(.*)
RewriteRule !^public/ public/%1

# Handle Front Controller... (as before)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]

Make sure you clear the browser cache before testing, since the previous (erroneous) 301 redirects will have been cached by the browser.

Change the 302 (temporary) redirects to 301 when you are sure it's working OK. (302s aren't cached by the browser, so makes testing easier.)

| improve this answer | |
  • Nope don't worked. if etc. app dir exists in root and in public, it rewrites to public/app.. ok, but when dir exists only in root i get: page isn’t working / ERR_TOO_MANY_REDIRECTS :/ – DariusJu Aug 27 '16 at 10:11
  • Rewrites to public/app or public/app/ (with a trailing slash)? "Normally" it should have a trailing slash if app exists as a directory in the root. You must have some code somewhere else? The code above (by itself) would not produce a rewrite loop and neither would the code in your question produce the output you state? Just to confirm, this .htaccess file is in the document root? Do you have any other .htaccess files along the filesystem path? Anything in your server config (eg. Alias directive)? – MrWhite Aug 27 '16 at 10:52
  • when i refresh page it adds slash, after next refresh removes and so on. the existing .htaccess is from laravel developers: github.com/mrakodol/Laravel-5-Bootstrap-3-Starter-Site/blob/… when i commented code after # Redirect Trailing Slashes If Not A Folder... now site.com/app returns 404 page.. with no loop i think that was a problem that i searched for. – DariusJu Aug 27 '16 at 11:08
  • "the existing .htaccess is from laravel developers:" - ah OK, that's the missing piece of the puzzle. The whole .htaccess file (specifically the mod_rewrite directives in this example) work as a whole. An interesting problem - I've updated my answer. – MrWhite Aug 29 '16 at 0:57
  • Would be interested to know if others have alternative ideas/solutions to this. – MrWhite Aug 29 '16 at 1:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.