3

I was taught that it is a good practice to separate publicly viewed webpages from included files and other, more sensitive information.

That poses a problem, however, when you wish to request index.php in the public folder.

Allow me to illustrate:

Site Root/
    Includes/
        -server_side_validation.php
    Public/
        -index.php
        JS/
           -js_form_validation.js
    Sandbox/
    Etc..

To get to index.php, I have to type in www.mywebsite.com/public/index.php, which is ugly.


So I learned a little about .htaccess and after a lot of trail and error I came to this code:

AddType application/x-httpd-php55 .php

# turn on rewrite engine
RewriteEngine on

## remove /public from host name
RewriteCond %{HTTP_HOST}    ^www.mywebsite.ca$ [NC,OR]
RewriteCond %{HTTP_HOST}    ^mywebsite.ca$
RewriteCond %{REQUEST_URI}  !public/
RewriteRule (.*)    /public/$1 [L]

What this apparently does is turn www.mywebsite.com/public/index.php into www.mywebsite.com, essentially "hiding" the /public/index.php part but still requesting index.php.

That all works dandy until I try to execute a pretty routine user registration from my index.php page. It goes like this:

On index.php, I have a registration form and a JS script that validates the form then sends an AJAX request to ../includes/registration_val.php to validate again on the server side before database submission.

The JS validation file is inside the public folder, inside a JS folder. So one directory deep inside the public folder.

When I attempt to submit the form to the server_side_validation.php from js_form_validation.js, I get 404 Not Found. Upon inspection I see that the request goes to /public/includes/registration_val.php.

So it seems to imply that my code seems to mistake public/ as the site root instead of actual site root.

Now when I bypass the above .htaccess code, the problem goes away. So somewhere in my .htaccess I'm boning the whole thing up and I'm hoping one of you programmers much smarter than I can come to my rescue so I can get things back on track.

I want to keep that nice little benefit of clearing away the /public/index.php directory in favor of a nice clean www.mywebsite.com but without screwing up any internal directory traversing.

Muchas gracias!!

2
  • 1
    I would try to rearrange your setup so that "Public" is document_root and also a php file to handle ajax requests should be also in the Public folder. You can even have that ajax handling PHP file "include" the file in the includes folder so that the process is easy. Mar 3, 2016 at 3:27
  • What are you using on your backend for development?
    – naneri
    Mar 3, 2016 at 10:33

1 Answer 1

4

As @Mike has already stated in comments, /public should be your DOCUMENT_ROOT and /includes should be entirely outside of your publicly viewable files, otherwise you are not really practising what you have been taught, to "separate publicly viewed webpages from included files and other, more sensitive information." Your files that you request via AJAX (publicly) should then go somewhere in the publicly accessible area.

That poses a problem, however, when you wish to request index.php in the public folder.

If /public is your document root, as it should be, then this is no longer a problem.

What this apparently does is turn www.mywebsite.com/public/index.php into www.mywebsite.com

The code shown above actually does the opposite. It quietly turns a request for www.mywebsite.com into www.mywebsite.com/public/. (mod_dir appends the index.php)

So it seems to imply that my code seems to mistake public/ as the site root instead of actual site root.

What your code does is prefix /public/ onto the URL if it doesn't already contain public/ - that is all. A request for /includes/registration_val.php doesn't contain public/, so the magic happens.

As mentioned above, /public should be your document root, however, as a quick fix, you can add a condition to exclude URLs that start /includes/ from being rewritten. Something like:

RewriteCond %{HTTP_HOST}    ^(www\.)?mywebsite\.ca$ [NC]
RewriteCond %{REQUEST_URI}  !^/public/
RewriteCond %{REQUEST_URI}  !^/includes/
RewriteRule (.*)    /public/$1 [L]

I've changed the condition so that it matches /public/ at the start of the URL, rather than anywhere in the URL, as you had initially.

(www\.)? - This combines your first two conditions by making the www subdomain optional. However, you should already have a canonical www/non-www redirect to resolve this anyway, so really you should only need to check for one or the other. And unless you have multiple hosts pointing to your webspace, you can remove this condition entirely.

(I assume your folders are really lowercase, as shown in your URLs. However, your directory structure shows capitalised first letters?)


Aside: It is always more efficient to do what you can in the RewriteRule pattern, rather than rely entirely on the RewriteCond directives above it. (.*) is going to match every request, so every request is going to be processed by this ruleset. This could be rewritten:

RewriteCond %{REQUEST_URI}  !^/includes/
RewriteRule !^public/ /public%{REQUEST_URI} [L]

This first checks that the URL-path does not start public/ (directory prefix removed), and only then are the conditions processed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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