Most shared hosting providers give you a fixed document root, usually public_html. This guide shows how to deploy Laravel without root access using two .htaccess files and a few hosting-safe configuration steps.
Most shared hosting providers, including Namecheap, Hostinger, and similar hosts, give you a fixed document root, usually public_html. You cannot point it at Laravel's public/ folder the way you would on a VPS.
This guide shows the standard workaround: upload the whole project as-is, and use two .htaccess files to route everything through public/ correctly.
The Folder Structure
Upload your entire Laravel project into public_html, or into a subfolder if it is an addon domain. It will look like this on the server:
public_html/
+-- app/
+-- bootstrap/
+-- config/
+-- database/
+-- public/
| +-- .htaccess (Laravel's default - keep as is)
| +-- index.php (needs small edits - see below)
| +-- ...
+-- resources/
+-- routes/
+-- storage/
+-- vendor/
+-- .env
+-- .htaccess (NEW - you create this)
+-- artisan
Two .htaccess files are doing the work here: one in the root that redirects every request into public/, and Laravel's original one inside public/ that stays untouched and handles the routing to index.php.
Step 1: Root .htaccess
Create this file at the project root, at the same level as app/, public/, and .env:
<IfModule mod_rewrite.c>
RewriteEngine On
# If the request is not for the public folder, redirect it there
RewriteCond %{REQUEST_URI} !^/public/
RewriteRule ^(.*)$ /public/$1 [L]
</IfModule>
If your Laravel project lives in a subfolder, for example an addon domain at public_html/blog, adjust the paths accordingly:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/blog/public/
RewriteRule ^(.*)$ /blog/public/$1 [L]
</IfModule>
Step 2: Keep public/.htaccess Untouched
Laravel ships this by default. Do not modify it:
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes
</IfModule>
RewriteEngine On
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>
Step 3: Edit public/index.php
The default index.php uses relative paths that assume public/ is one level below the root. On some shared hosts, especially when using the root redirect trick above, path resolution can still work fine. If you hit class-not-found or blank white-screen errors, explicitly point it at the correct paths:
<?php
use Illuminate\Contracts\Http\Kernel;
use Illuminate\Http\Request;
define('LARAVEL_START', microtime(true));
// Point directly at vendor/autoload.php one level up
require __DIR__.'/../vendor/autoload.php';
// Bootstrap the app the same way
$app = require_once __DIR__.'/../bootstrap/app.php';
$kernel = $app->make(Kernel::class);
$response = $kernel->handle(
$request = Request::capture()
)->send();
$kernel->terminate($request, $response);
This is nearly identical to Laravel's default. The key thing to check is that __DIR__.'/../vendor/autoload.php' and __DIR__.'/../bootstrap/app.php' correctly resolve to your uploaded folder structure. If you renamed or nested folders differently, adjust these two paths.
Step 4: Environment File
Upload .env to the project root, not inside public/. Set:
APP_ENV=production
APP_DEBUG=false
APP_URL=https://yourdomain.com
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_DATABASE=your_db_name
DB_USERNAME=your_db_user
DB_PASSWORD=your_db_password
Most shared hosts, including Hostinger and Namecheap, give you phpMyAdmin. Create the database and user there first, then run migrations via SSH or terminal if your plan includes it. If it does not, import an SQL dump through phpMyAdmin.
Step 5: Storage Link Without Symlink Support
Laravel normally uses php artisan storage:link, which creates a symlink from public/storage to storage/app/public. Many shared hosts either do not support symlinks or reset them.
If storage linking is available through terminal or SSH, run:
php artisan storage:link
If symlink support is not available, manually create a public/storage folder and copy files there instead of relying on the symlink, or configure a route to serve files directly:
// routes/web.php
Route::get('/storage/{path}', function ($path) {
$file = storage_path('app/public/' . $path);
abort_unless(file_exists($file), 404);
return response()->file($file);
})->where('path', '.*');
Step 6: File Permissions
Set these folders to be writable. Usually 755 works, but some hosts need 775 depending on ownership:
storage/
storage/framework/
storage/framework/cache/
storage/framework/sessions/
storage/framework/views/
storage/logs/
bootstrap/cache/
Most hosting file managers let you right-click a folder, open permissions, and apply changes recursively.
Step 7: Clear and Cache Config
If you have terminal access after upload, run:
php artisan config:cache
php artisan route:cache
php artisan view:cache
If you do not have terminal access, skip caching. Laravel will still work without it, just slightly slower.
Common Errors and Fixes
| Error | Likely Cause | Fix |
|---|---|---|
| 500 Internal Server Error | Wrong permissions on storage/ or bootstrap/cache/ |
Set to 755 or 775 |
| White blank page | APP_DEBUG=false hiding a real error |
Temporarily set APP_DEBUG=true, then check storage/logs/laravel.log |
| Class not found | vendor/ missing or autoload path wrong in index.php |
Re-upload the vendor/ folder and verify the paths in Step 3 |
| Assets not loading | APP_URL mismatch or asset paths pointing to the wrong folder |
Set the correct APP_URL in .env and run npm run build before upload |
Quick Checklist Before Going Live
- Root
.htaccesscreated and redirects to/public/. public/.htaccessleft as Laravel's default..envuploaded with correct database andAPP_URL.- Database created and migrated through terminal or SQL import.
storage/andbootstrap/cache/permissions set.APP_DEBUG=falsebefore going live.- SSL certificate active. Most shared hosts offer free Let's Encrypt certificates.
This setup gets a full Laravel app running on standard shared hosting without needing root or document-root changes. The same approach works whether you are on Namecheap, Hostinger, or most cPanel-based hosts.