php.htaccessxampphttpd.conf

Proper .htaccess files for using a public folder under main php project folder


I have Xampp installed on a Windows 10 PC for practicing web development. I've been following along with Jeffrey Way from Laracasts' PHP for Beginners course as a refresher and also to see how he recommends structuring folders and configuring a router.

I know little about Apache and .htaccess files but remember that changing them was necessary for another MVC/router/php project I was successful in completing a year ago. I'd hoped to just re-apply with this course but have run into some difficulties and could use some help.

To begin, I decided to create a demo folder under xampp's htdocs folder for housing the course's code. I HAD EVERYTHING WORKING through episode 29 and then Jeffrey decided to move the main index.php into a public folder underneath the main project folder (e.g. xampp/htdocs/demo/public). This leaves essential php files such as a config.php and Class files in the original root of the project (xampp/htdocs/demo). He used the php CLI I believe, to redefine the project root when he started the server. He is NOT using xampp. I'm hoping to be able to do reproduce his results with .htacccess files and httpd.conf for Apache as needed. I began implementing changes as best I understood while referencing files from my old project.

Project folder structure is as follows:

Entering localhost/demo in the address bar of Chrome brings me to the proper Home page. Navigation has four links and ALL WORKED FINE before moving the main index.php to the public folder:

After the course's required changes, Home works fine but the other 3 nav links yield 404 not found errors.

xampp/demo .htaccess:

# Redirect requests to public folder
RewriteEngine On
    RewriteCond %{REQUEST_URI} !public
    RewriteRule (.*) public/$1 [L]

xampp/demo/public .htaccess:

    # Remove index.php and question mark from the request, enabling pretty or "vanity" URLs
    RewriteEngine On
    RewriteBase /demo
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-l
    RewriteRule ^(.*)$ /index.php?$1 [L,QSA]

httpd.conf additions:

    # php for beginners, laracasts 8/22/2023
    <VirtualHost phpdemo:80>
        DocumentRoot "C:/xampp/htdocs/demo"
        ServerName localhost
    
        <Directory "C:/xampp/htdocs/demo">
            Options FollowSymLinks
            AllowOverride All
            Require all granted
        </Directory>
    </VirtualHost>

I tried working off of my old project that was very successful and cannibalized the .htaccess and httpd.conf files. I'm out of my element and would like some assistance in both understanding and solutions. The nav links do not include a .php extension and, again, were working. I do not know how to handle this in the .htaccess files.

All constructive input appreciated! Please keep in mind this issue has cropped up in the middle of a course and changing structure, etc., shouldn't be done at this time.


Solution

  • DocumentRoot "C:/xampp/htdocs/demo"
    

    If this is your own server then you would simply change the DocumentRoot to point directly to the /public directory. For example:

    DocumentRoot C:/xampp/htdocs/demo/public
    

    You would then need to change the corresponding <Directory "C:/xampp/htdocs/demo"> container to match this.


    Alternatively, if you do not have access to the server config (a shared server environment perhaps) then you can modify the .htaccess files to rewrite the request to the /public subdirectory. Currently, your main problem is this line (in /demo/public/.htaccess):

    RewriteRule ^(.*)$ /index.php?$1 [L,QSA]
    

    The slash prefix on the substitution string (2nd argument) is rewriting the request back to the root, not the public subdirectory. You need to either specify the root-relative URL-path here (eg. /demo/public/index.php?$1) or remove the slash prefix entirely so that it is relative to the directory that contains the .htaccess file OR the value of RewriteBase if defined (eg. index.php?$1). However, the RewriteBase directive is set incorrectly (it would need to be RewriteBase /demo/public). But this is not required at all here, so should be removed entirely (which simplifies the directives and avoids the config file being dependent on the directory it is contained within).

    So, you can simplify the .htaccess files to the following:

    # /demo/.htaccess
    
    RewriteEngine On
    
    # Rewrite requests to public folder
    RewriteRule (.*) public/$1 [L]
    

    The original condition (RewriteCond directive) is not required, since the presence of the 2nd .htaccess file in the /public subdirectory is enough to prevent a rewrite loop (mod_rewrite is not inherited by default).

    Then, you also have:

    # /demo/public/.htaccess
    
    RewriteEngine On
    
    # Front-controller pattern (enabling "vanity" URLs)
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-l
    RewriteRule (.*) index.php?$1 [L,QSA]
    

    (The start-of-string and end-of-string anchors around (.*) are not necessary.)