Skip to content

Instantly share code, notes, and snippets.

@rafaucau
Last active March 26, 2025 12:25
Show Gist options
  • Save rafaucau/8259400871607bfb0826ba7f40b28474 to your computer and use it in GitHub Desktop.
Save rafaucau/8259400871607bfb0826ba7f40b28474 to your computer and use it in GitHub Desktop.
Installing Laravel in a subfolder

Installing Laravel 10 in a subfolder

Installing Laravel in a domain subfolder can be useful in certain situations, although it is not officially supported by Laravel creators. In this instruction, I will describe how to configure Laravel 10 in a subfolder and additional tools such as Livewire v3.

Table of Contents

  1. Laravel Basse
  2. Livewire
  3. Filament Panel Builder

Laravel Base

Create a .htaccess file in the main installation folder. In this file, rewrite all requests to the public folder:

RewriteEngine On
RewriteRule ^(.*)$ public/$1 [L]

Update the .env file by adding your folder name to the links. Additionally, create APP_DIR with the folder name:

APP_DIR=subdir
APP_URL="http://localhost/${APP_DIR}"
ASSET_URL="${APP_URL}"

Add your subdirectory env variable to config/app.php:

return [
    //...
    'dir' => env('APP_DIR'),
    //...
];

It's a good idea to create a helper that will add a prefix to the paths. I did it in app/Helpers/RouteHelper.php:

use Illuminate\Support\Str;

class RouteHelper
{
    public static function getDirPathPrefix(?string $path = ''): string
    {
        $appDir = config('app.dir');

        if (! $appDir) {
            return $path;
        }

        if (Str::length($path) === 0) {
            return $appDir;
        }

        if (Str::startsWith($path, '/')) {
            $appDir = Str::start($appDir, '/');
        }

        return $appDir.Str::start($path, '/');
    }
}

In the app/providers/RouteServiceProvider.php file, modify the paths by adding prefixes:

$this->routes(function () {
    Route::middleware('api')
+       ->prefix(RouteHelper::getDirPathPrefix('api'))
        ->group(base_path('routes/api.php'));

    Route::middleware('web')
+       ->prefix(RouteHelper::getDirPathPrefix())
        ->group(base_path('routes/web.php'));
});

If you use the HOME constant from RouteServiceProvider in the code, change it to a static variable, and then update all occurrences of RouteServiceProvider::HOME to RouteServiceProvider::$HOME:

public static string $HOME = '/dashboard';

public function __construct($app)
{
    self::$HOME = RouteHelper::getDirPathPrefix('dashboard');
    parent::__construct($app);
}

Livewire

Create a new file named web_custom.php in the routes folder:

use Illuminate\Support\Facades\Route;
use Livewire\Features\SupportFileUploads\FilePreviewController;
use Livewire\Features\SupportFileUploads\FileUploadController;
use Livewire\Livewire;

Livewire::setScriptRoute(fn ($handle) => Route::get('/livewire/livewire.js', $handle));

Livewire::setUpdateRoute(fn ($handle) => Route::post('/livewire/update', $handle));

Route::post('/livewire/upload-file', [FileUploadController::class, 'handle'])
    ->name('livewire.upload-file');

Route::get('/livewire/preview-file/{filename}', [FilePreviewController::class, 'handle'])
    ->name('livewire.preview-file');

In the routes/web.php file, include the web_custom.php file at the end of the file:

require __DIR__.'/web_custom.php';

In this section, the routes are re-declared, which allows Laravel to automatically prepend the subdirectory prefix set earlier in the RouteServiceProvider. This way, there's no need to use RouteHelper::getDirPathPrefix in this section.

Filament Panel Builder

After configuration according to the documentation, the admin panel will return a 404. In the app/Providers/AdminPanelProvider.php file, you need to change the path:

class AdminPanelProvider extends PanelProvider
{
    public function panel(Panel $panel): Panel
    {
        return $panel
            ->default()
            ->id('admin')
            ->path(RouteHelper::getDirPathPrefix('admin'))
            //...
    }
}

⚠️ Filament uses Livewire, so to be able to log in or not fall into an infinite loop of redirects, you need to change the paths as described in the Livewire section above.

@iron-viper
Copy link

Hello, found 1 issue, when I do SomePage::getUrl([id]) in the core it calls route helper and return the route depend on APP_URL without subfolder

@iannacone
Copy link

Hello, found 1 issue, when I do SomePage::getUrl([id]) in the core it calls route helper and return the route depend on APP_URL without subfolder

indeed APP_URL="http://localhost/${APP_DIR}" must contain ${APP_DIR}

@anishsarkar
Copy link

Hi, great work. Could you help me how to do it Laravel 11+. I followed the instructions in Laravel 11 to configure route: added the following in bootstrap/app.php

->withRouting(

        // web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',

        using: function () {
                Route::middleware('web')
                        ->prefix(config('app.global_route_prefix'))
                        ->group(base_path('routes/web.php'));

                
                Route::middleware('web')
                        ->prefix(config('app.global_route_prefix'))
                        ->group(base_path('routes/web_livewire.php')); 
                    
        }

    )

My web_livewire.php file:



Livewire::setScriptRoute(fn ($handle) => Route::get('/livewire/livewire.js', $handle));

Livewire::setUpdateRoute(fn ($handle) => Route::post('/livewire/update', $handle));

Route::post('/livewire/upload-file', [FileUploadController::class, 'handle'])
    ->name('livewire.upload-file');

Route::get('/livewire/preview-file/{filename}', [FilePreviewController::class, 'handle'])
    ->name('livewire.preview-file');


This adds the routes in the php artisan route:list with the prefix that I have specified. But my routes are being duplicated for these livewire routes without the prefix. They seem to have the same name.


GET|HEAD   bsds/livewire/livewire.js ....................................................... Livewire\Mechanisms › FrontendAssets@returnJavaScriptAsFile
GET|HEAD   bsds/livewire/preview-file/{filename} .............................. livewire.preview-file › Livewire\Features › FilePreviewController@handle
POST       bsds/livewire/update .................................................... livewire.update › Livewire\Mechanisms › HandleRequests@handleUpdate
POST       bsds/livewire/upload-file ............................................ livewire.upload-file › Livewire\Features › FileUploadController@handle
GET|HEAD   filament/exports/{export}/download ................................................ filament.exports.download › Filament\Actions › DownloadExport
GET|HEAD   filament/imports/{import}/failed-rows/download .............. filament.imports.failed-rows.download › Filament\Actions › DownloadImportFailureCsv
GET|HEAD   livewire/livewire.js ................................................................ Livewire\Mechanisms › FrontendAssets@returnJavaScriptAsFile
GET|HEAD   livewire/livewire.min.js.map .......................................................................... Livewire\Mechanisms › FrontendAssets@maps
GET|HEAD   livewire/preview-file/{filename} ....................................... livewire.preview-file › Livewire\Features › FilePreviewController@handle
POST       livewire/update ............................................................. livewire.update › Livewire\Mechanisms › HandleRequests@handleUpdate
POST       livewire/upload-file ..................................................... livewire.upload-file › Livewire\Features › FileUploadController@handle

Also, as you see filament exports route is also not being getting the prefix ``` bsds `` (my chosen prefix). My file uploads are failing in filament forms, this may be the cause, though it is not causing stack trace in log file. Any help would be great. Thanks in advance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment