How to Deploy a Laravel Website on Shared Hosting (Namecheap, Hostinger) Without Root Access

Nawjesh Soyeb Deployment Hostinger Laravel Namecheap PHP Shared Hosting

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 .htaccess created and redirects to /public/.
  • public/.htaccess left as Laravel's default.
  • .env uploaded with correct database and APP_URL.
  • Database created and migrated through terminal or SQL import.
  • storage/ and bootstrap/cache/ permissions set.
  • APP_DEBUG=false before 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.