How to Host a Laravel Project on a Shared Hosting Server
Understanding the Directory Structure
By default, shared hosting servers place public assets in a directory named public_html (or www). On the other hand, a fresh Laravel installation has its public files under a subfolder named public/, containing the main entry file index.php. The rest of the Laravel application logic, configuration, and sensitive environment secrets (.env) reside in the root directory, which should never be directly accessible from the web.
Exposing the root folder on the web is a severe security risk. A malicious user could read your environment files, database passwords, or application keys. The goal is to deploy the Laravel core files outside of public_html and map the contents of Laravel's public/ directory into public_html.
Splitting Core & Public Directories
The safest approach on cPanel-based shared hosting is to split the Laravel structure:
- Place all Laravel files and folders (except
public/) into a new directory in your user home, e.g.,/home/user/laravel-core/. - Move the contents of Laravel's
public/directory directly into/home/user/public_html/.
After moving the files, you must modify index.php in /home/user/public_html/ to point to the new location of the vendor/ and bootstrap/ folders. Open the file and adjust the paths:
// Register The Auto Loader require __DIR__.'/../laravel-core/vendor/autoload.php'; // Run The Application $app = require_once __DIR__.'/../laravel-core/bootstrap/app.php';
If you are using Laravel 11, the bootstrap configuration handles paths differently. Make sure your application's public path is explicitly bound in bootstrap/app.php if you move the public folder, using:$app->usePublicPath(base_path('../public_html'));
Configuring Environment & Running Migrations
Once directories are set up, configure your environment secrets:
- Rename
.env.exampleto.envin the/home/user/laravel-core/directory. - Edit the
.envfile using the cPanel File Manager to specify database credentials and setAPP_ENV=productionandAPP_DEBUG=false.
Because shared hosting often restricts terminal and SSH access, you cannot run php artisan migrate directly. Instead, you can temporarily register a custom route in routes/web.php to trigger migrations via the browser:
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Route;
Route::get('/run-migrations', function () {
try {
Artisan::call('migrate --force');
return 'Database migrated successfully: ' . Artisan::output();
} catch (\Exception $e) {
return 'Migration error: ' . $e->getMessage();
}
});Always remove this route immediately after use, or wrap it behind robust authentication middleware to prevent unauthorized database resets!
Creating the Storage Symlink via Web Routes
Laravel uses a symbolic link from public/storage to storage/app/public to serve user-uploaded files. In a standard setup, you'd run php artisan storage:link. Since you don't have terminal access, you can trigger this link creation using the same route-based approach in routes/web.php:
Route::get('/link-storage', function () {
try {
Artisan::call('storage:link');
return 'Storage symbolic link created successfully!';
} catch (\Exception $e) {
return 'Symlink error: ' . $e->getMessage();
}
});Once you visit yourdomain.com/link-storage in the browser, the symlink will be generated automatically, enabling media upload and retrieval capabilities on shared hosting.