Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Hondaman900's avatar

I broke my Laravel app root and can't figure out how or why

I'm working on an app using Laragon. I had everything working and progressing well. I ran into an issue with PHP version mismatches between Composer and my app that turned out to be a difference between the user and system $PATH environmental variables. But somewhere along the way I broke my site pointers to my app's public folder root.

Instead of bringing up my index blade like it used to, as directed by my controller and route, I'm getting an "Index of" file listing page. So, been there many times before, but this time the usual solutions are not working. This is not a situation where the server needs to have the public folder as it's root, it already does, it just needs to not list files and show the index blade. I'm getting a listing of the "virtual" public folder, just showing "storage". In the storage folder it just shows .gitnore and my images folder, so it's directory-listing my public folder.

FWIW here's my .htaccess file:

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

# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

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

# Send Requests To Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]

I can't figure out what I changed to make this happen and how to fix it. I know this is sorta vague, but any suggestions as to how I can fix or troubleshoot this will be very welcome.

0 likes
21 replies
Snapey's avatar

Sounds like your server does not know how to handle php files.

What happens if you directly visit index.php?

1 like
Hondaman900's avatar

I can't get from here to there. The root URL places me in public. Index.php is back up one level which is not visible. If I quit Laragon and use artisan serve, I can see my index page, but subsequent pages are 404s. That may be because my database is back in Laragon's MySQL server.

1 like
technofreaks's avatar

@Hondaman900

For checking php version and modules: create phpinfo.php in current root folder which being served (in public) phpinfo.php content

<?php

phpinfo();

?>

save file, then type in browser URL/phpinfo.php , this will show information and modules, php version and document root can be verified.

check htaccess rewrite , document here (copy paste in browser URL bar)

acquia.my.site.com/s/article/360004303994-Basic-rewrite-rule-examples
1 like
Snapey's avatar

The root URL places me in public. Index.php is back up one level which is not visible

Then your directory layout is messed up. Public should be the document root, and index.php and .htaccess must be in that public folder.

Hondaman900's avatar

@Snapey Yes, that's my issue, it certainly is. If I move .htaccess to 'public', it completely breaks. I get:

Internal Server Error
The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at [email protected] to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.

The Apache access log shows: 127.0.0.1 - - [30/Apr/2024:08:10:14 -0400] "GET /storage/ HTTP/1.1" 500 530

The Apache error log shows: [Tue Apr 30 08:18:23.572396 2024] [core:error] [pid 18516:tid 1224] [client 127.0.0.1:65043] AH00124: Request exceeded the limit of 10 internal redirects due to probable configuration error. Use 'LimitInternalRecursion' to increase the limit if necessary. Use 'LogLevel debug' to get a backtrace., referer: http://stripe-laravel.test/

I think my issue relates to the Artisan mapping of 'storage', because the 'public' root is mapping to 'storage' inside 'public'. Does that not mean that placing my .htaccess file in 'public' effectively puts it in 'storage' (does that expose it?) which wouldn't work as it has to be at the app root...?

I placed my phpinfo.php in my 'public' folder, and when I click on it my browser shows http://stripe-laravel.test/storage/phpinfo.php as the file's URL/location.

@technofreaks phpinfo.php in the 'public' folder shows some interesting things, such as the following:

As mentioned above, the file's URL is: http://stripe-laravel.test/storage/phpinfo.php

For Apache Environment
HTTP_HOST	stripe-laravel.test
HTTP_REFERER	http://stripe-laravel.test/storage/
SERVER_NAME	stripe-laravel.test
DOCUMENT_ROOT	C:/laragon/www/stripe-laravel/public
SCRIPT_FILENAME	C:/laragon/www/stripe-laravel/public/storage/phpinfo.php
REQUEST_URI	/storage/phpinfo.php
SCRIPT_NAME	/storage/phpinfo.php

For HTTP Headers Information
HTTP Request	GET /storage/phpinfo.php HTTP/1.1
Host	stripe-laravel.test
Referer	http://stripe-laravel.test/storage/
X-Powered-By	PHP/8.1.10

I don't think the rest of phpinfo is relevent.

I think maybe my Artisan storage link is broken or messed up...?

technofreaks's avatar

@Hondaman900 Not seen that before, I am on Linux

try

php artisan cache:clear

I checked using this..

Auth::routes(['register' => false]);
Route::get('phpmyinfo', function () {
    phpinfo();
})->name('phpmyinfo');

shows correct for me

$_SERVER['DOCUMENT_ROOT']	/home/suds/laravel/dev/public
_SERVER['SCRIPT_FILENAME']	/home/suds/laravel/dev/public/index.php
gych's avatar

@Hondaman900 Which results do you get when you delete the storage symlink folder from public folder ?

Hondaman900's avatar

@technofreaks clearing cache and/or config makes no diff. I added the route you suggested and it doesn't work (Not Found). None of my routes work. If I run phpinfo.php (by clicking on it in the exposed file listing) I get

$_SERVER['DOCUMENT_ROOT']	C:/laragon/www/stripe-laravel/public
$_SERVER['SCRIPT_FILENAME']	C:/laragon/www/stripe-laravel/public/storage/phpinfo.php
Hondaman900's avatar

@gych I don't have a symlink folder (Windows) but I can see the "symlink" for storage in the config\filesystems.php file. It's the same however as my other working Laravel apps:

    /*
    |--------------------------------------------------------------------------
    | Default Filesystem Disk
    |--------------------------------------------------------------------------
    |
    | Here you may specify the default filesystem disk that should be used
    | by the framework. The "local" disk, as well as a variety of cloud
    | based disks are available to your application. Just store away!
    |
    */

    'default' => env('FILESYSTEM_DRIVER', 'local'),

    /*
    |--------------------------------------------------------------------------
    | Filesystem Disks
    |--------------------------------------------------------------------------
    |
    | Here you may configure as many filesystem "disks" as you wish, and you
    | may even configure multiple disks of the same driver. Defaults have
    | been setup for each driver as an example of the required options.
    |
    | Supported Drivers: "local", "ftp", "sftp", "s3"
    |
    */

    'disks' => [

        'local' => [
            'driver' => 'local',
            'root' => storage_path('app'),
        ],

        'public' => [
            'driver' => 'local',
            'root' => storage_path('app/public'),
            'url' => env('APP_URL').'/storage',
            'visibility' => 'public',
        ],

        's3' => [
            'driver' => 's3',
            'key' => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
            'region' => env('AWS_DEFAULT_REGION'),
            'bucket' => env('AWS_BUCKET'),
            'url' => env('AWS_URL'),
            'endpoint' => env('AWS_ENDPOINT'),
            'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
        ],

    ],

    /*
    |--------------------------------------------------------------------------
    | Symbolic Links
    |--------------------------------------------------------------------------
    |
    | Here you may configure the symbolic links that will be created when the
    | `storage:link` Artisan command is executed. The array keys should be
    | the locations of the links and the values should be their targets.
    |
    */

    'links' => [
        public_path('storage') => storage_path('app/public'),
    ],
gych's avatar

@Hondaman900 If you use php artisan storage:link, a symbolic link between storage/app/public and public/storage is created.

I also use Laragon on Windows for my projects and I have a symbolic link between those two folders. Try to delete the storage folder from your public folder and see which results you get after that.

Hondaman900's avatar

@gych My apologies, I do see the symlink/shortcut file in my app's 'public' folder.

Deleting it just removes it from the browser file listing. Renaming it shows the new name and clicking it still goes to the real storage folder. Removing or renaming the actual storage folder gives a 'Forbidden' error when the link is clicked.

The correct way for this to work is that the symlink redirects the browser to the storage folder. There's no redirection and the files are all exposed. Not sure if Laravel or Apache is broken, or both.

I'm starting to think I may have to start over.

Randy_Johnson's avatar

You should have 2 files, app, public html. Public html should be a soft link to your app/public.

Hondaman900's avatar

@Randy_Johnson Unfortunately I don't have a backup. This was an isolated test site to get Stripe webhooks working and I could start over, though that would loose me a day or so. I was hoping to locate some single dumb misplaced configuration that would fix this and I could move forward. I also feel the need to figure out what happened and learn from that. I usually manage my projects with SourceTree and Bitbucket, but at the time this quick test didn't warrant that, until it grew and did. I'm now kicking myself for not doing so.

Not sure what 2 files you refer to. My app/public only has the phpinfo.php I just put in there (see above). Otherwise it's empty.

Snapey's avatar

@Hondaman900 @randy is wrong.

Look, as I said, your apache server should serve the public folder as the document root.

The public folder should contain the index.php file and the .htaccess file. There should be NO OTHER index.php or .htaccess

You should have ONE symlink. This is the one that connects storage/app/public to public/storage. If you are not saving anything in the storage/app/public folder then you can ignore this step.

Hondaman900's avatar

I'm getting lost in the layers of Laragon, Apache Laravel and their relative location requirements and mapping.

If I move the .htaccess and index.php to Public, I get "This site can't be reached" errors. This is using Laragon's URL for the app http://stripe-laravel.test/

If I look at localhost I see Laragon's server page. If I go to localhost/stripe-laravel I see the site files listed under the app root. If I go to 'localhost/stripe-laravel/publicit tries to runindex.php` which bombs out with errors, line by line, like the framework is not running or processing the php code.

Please or to participate in this conversation.