Deploying Laravel on Shared Hosting (No SSH Required)

Published: (February 7, 2026 at 07:04 AM EST)
4 min read
Source: Dev.to

Source: Dev.to

Cover image for Deploying Laravel on Shared Hosting (No SSH Required)

Why Laravel Deployment on Shared Hosting Is Different

1. Project File Structure

A typical Laravel project looks like this:

project/
 ├── app/
 ├── bootstrap/
 ├── config/
 ├── public/
 ├── storage/
 ├── vendor/
 └── artisan

However, shared hosting usually exposes only one public directory:

├── public_html/

2. No SSH, No Commands

If there is no SSH access, you cannot:

  • Install Composer dependencies
  • Build assets (npm run …)
  • Run migrations
  • Clear or optimize caches

How to Solve the Situation – Step‑by‑Step

Step 1: Prepare Your Application Locally

Clone your Laravel project into a production‑ready copy so you don’t affect your development setup:

├── laravel_app/
├── laravel_app_prod/

Inside laravel_app_prod, prepare the application for production:

# clearing the cache
php artisan config:clear
php artisan cache:clear
php artisan route:clear
php artisan view:clear

# installing production dependencies and optimizing
composer install --no-dev --optimize-autoloader

# building frontend assets
npm run build

Step 2 – Re‑organize Project File Structure

To match the shared‑hosting layout, move only the contents of the public/ folder into the new public_html/ folder:

public_html/
handleRequest(Illuminate\Http\Request::capture());

Important: Incorrect paths will cause a 500 error.

Step 4 – Set Up the Database Using cPanel

  1. Open MySQL Database Wizard in cPanel.
  2. Create a database.
  3. Create a database user and assign ALL PRIVILEGES.

Then update the .env file inside laravel_app_prod:

APP_NAME=MyApp
APP_ENV=production
APP_DEBUG=false          # very important (security)
APP_URL=https://your-app-domain

DB_DATABASE=database_name
DB_USERNAME=database_user
DB_PASSWORD=database_password

⚠️ Never enable APP_DEBUG=true in production.

Step 5 – Temporary Deploy Route (Token Protected)

Because there is no SSH, you can temporarily execute Artisan commands via a protected web route. Remove this route immediately after deployment.

  1. Add a secret token to .env:

    DEPLOY_TOKEN=verySecretRandomToken123
  2. Add the route in routes/web.php:

    use Illuminate\Support\Facades\Artisan;
    use Illuminate\Support\Facades\Route;
    
    Route::get('/deploy/{token}', function ($token) {
        abort_unless($token === env('DEPLOY_TOKEN'), 403);
    
        // 1. Run migrations & clear cache
        Artisan::call('migrate', ['--force' => true]);
        Artisan::call('optimize:clear');
    
        // 2. Fix storage link (custom fix)
        // We point to the 'public_html' folder using $_SERVER['DOCUMENT_ROOT']
        $targetFolder = storage_path('app/public');
        $linkFolder   = $_SERVER['DOCUMENT_ROOT'] . '/storage';
    
        if (!file_exists($linkFolder)) {
            symlink($targetFolder, $linkFolder);
            $storageStatus = 'Storage link created successfully.';
        } else {
            $storageStatus = 'Storage link already exists.';
        }
    
        return "Deployment completed.\nMigrations run.\nCache cleared.\n" . $storageStatus;
    });

Step 6 – Upload Files to the Server

Using cPanel File Manager:

  1. Zip laravel_app_prod/.
  2. Zip the contents of public/.
  3. Upload both archives:
    • Extract laravel_app_prod outside public_html.
    • Extract the public files inside public_html.

Why place laravel_app_prod outside public_html?
This keeps your core application code out of the web‑accessible directory, providing an essential security layer.

Step 7 — Set Critical Permissions

Laravel needs write access to a few folders. If they aren’t set correctly, logs won’t write and sessions won’t be saved.

  1. In cPanel File Manager, navigate to laravel_app_prod/storage.
  2. Right‑click → Change Permissions.
  3. Set the permissions to 775 (User: Read/Write/Execute, Group: Read/Write/Execute, World: Read/Execute).
  4. Repeat the same steps for laravel_app_prod/bootstrap/cache.

⚠️ Note: Never set these folders to 777. That is a major security risk on shared hosting.

Step 8 — Run Migrations and Remove the Deploy Route

Open the following URL in a browser (replace your-domain.com with your actual domain):

https://your-domain.com/deploy/verySecretRandomToken123

If everything is correct you’ll see:

Deployment completed successfully

Immediately after that:

  • Delete the deploy route.
  • Remove DEPLOY_TOKEN from .env.

These steps are critical for security.

Common Issues Faced

500 Internal Server Error

  • Incorrect file paths in index.php.
  • Wrong PHP version (use cPanel MultiPHP Manager).
  • Incorrect folder permissions (storage 775, bootstrap/cache 775).

403 Unauthorized

  • Unmatched route token with DEPLOY_TOKEN.

Final Thoughts

Shared hosting isn’t ideal for Laravel, but it’s still common for:

  • Client projects
  • MVPs
  • Budget‑constrained deployments

With proper preparation and a secure workflow, Laravel applications can run reliably without SSH access.

💡 Pro Tip – Safer Database Alternative

If you’re uncomfortable running migrations via a web route (which carries security risks), use the Export/Import method:

  1. Run migrations on your local machine.
  2. Export the local database as an .sql file.
  3. In cPanel → phpMyAdmin on the server, import the .sql file directly.

This avoids executable logic in production routes.

🔗 Stay Connected

Follow me for more Laravel tutorials, dev tips, deployment workflows, and real‑world production solutions:

  • LinkedIn:
  • Medium: (and join my mailing list: )

Found this article useful?

🙏 Show your support by clapping 👏, subscribing 🔔, and sharing on social networks.

0 views
Back to Blog

Related posts

Read more »