Skip to content
Deployment⏱️ 13 min read·

How to Deploy a Laravel Project on cPanel Safely (No Security Flaws or Exposed .env)

Learn the proper, secure way to deploy Laravel applications on shared cPanel hosting. Avoid the rookie mistake of dumping your whole project into public_html and exposing sensitive .env files.

Need this on a server? See deployment and maintenance.

MN
Manoj NeupaneSoftware Developer & Programming Instructor
#Laravel#cPanel#PHP#MySQL#Deployment#Nepal
🎯 Who This Tutorial Is For

Laravel developers and students hosting applications on shared cPanel hosting commonly purchased in Nepal (Himalayan Host, Prabhu Host, Nest Nepal, etc.).

📋 Prerequisites
  • ✓A working Laravel 10 or 11 project
  • ✓Access to a cPanel hosting account with File Manager and MySQL Database Wizard
  • ✓PHP 8.2 or 8.3 enabled on cPanel

🚀 What You Will Learn & Build

  • ▸The critical security risks of incorrect cPanel directory structure
  • ▸Separating core application code from public document root
  • ▸Configuring the index.php entry point and storage symlink
  • ▸Creating MySQL databases and setting up the production .env file
  • ▸Fixing common 500 errors and permission problems
1

1. The Critical cPanel Security Trap: Never Dump Everything into public_html

Most shared cPanel tutorials tell beginners to copy their entire Laravel project folder directly into `public_html`. **This is a critical security vulnerability!** When your entire project lives inside `public_html`: - Visitors can download your private database credentials and API keys by browsing directly to `https://yourdomain.com/.env`! - Attackers can view proprietary business logic, logs, and vendor code. The correct, professional architecture places all core Laravel code **outside** of `public_html`, and copies **only the contents of Laravel's `public/` directory** into `public_html`.
🔒 Security Notice— Public Document Root Isolation

In standard Laravel, only the public/ directory is meant to be web-accessible. Your app/, config/, storage/, and .env files must remain outside public_html.

2

2. Step-by-Step Secure Deployment Process

### Step A: Prepare Your Project Archive Locally 1. Run `npm run build` locally to compile production assets (Vite/Tailwind). 2. Clean temporary caches: ```bash php artisan config:clear php artisan route:clear php artisan view:clear ``` 3. Create a ZIP archive of your project **excluding** the `node_modules` folder and the local `.env` file. ### Step B: Upload Core Files Outside public_html 1. Log into your cPanel and open **File Manager**. 2. Navigate to your home directory (e.g., `/home/username/`, one level above `public_html`). 3. Create a new folder named `laravel_app`. 4. Upload your project ZIP file inside `/home/username/laravel_app/` and extract it. ### Step C: Move Public Assets into public_html 1. Move the contents of `/home/username/laravel_app/public/` into your `public_html/` directory (including `.htaccess` and `index.php`). 2. Delete the empty `public/` folder inside `laravel_app` or leave it.
3

3. Updating index.php Paths

Since your core Laravel files are now located in `../laravel_app/`, you need to edit `public_html/index.php` so it can find the autoloader and bootstrap file:
public_html/index.php
<?php

use Illuminate\Http\Request;

define('LARAVEL_START', microtime(true));

// Determine if the application is in maintenance mode...
if (file_exists($maintenance = __DIR__.'/../laravel_app/storage/framework/maintenance.php')) {
    require $maintenance;
}

// Register the Composer autoloader...
require __DIR__.'/../laravel_app/vendor/autoload.php';

// Bootstrap Laravel and handle the request...
(require_once __DIR__.'/../laravel_app/bootstrap/app.php')
    ->handleRequest(Request::capture());
4

4. Database Creation & Production .env Setup

1. In cPanel, open **MySQL Database Wizard**. 2. Create a database (e.g., `username_laraveldb`). 3. Create a user (e.g., `username_dbuser`) with a strong password. 4. Assign the user to the database with **ALL PRIVILEGES**. 5. In File Manager, navigate to `/home/username/laravel_app/` and create or edit the `.env` file:
laravel_app/.env
APP_NAME="My App"
APP_ENV=production
APP_KEY=base64:yourGeneratedKeyHere...
APP_DEBUG=false
APP_URL=https://yourdomain.com

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=username_laraveldb
DB_USERNAME=username_dbuser
DB_PASSWORD=YourStrongDatabasePassword123!

✨ Expected Output & Verification

Visiting https://yourdomain.com loads your Laravel home page smoothly with all CSS, images, and database queries executing without any 500 error. Visiting https://yourdomain.com/.env returns an HTTP 404 (file does not exist in public_html).

🧪 How to Test the Implementation

  1. 1Open https://yourdomain.com in browser and check homepage rendering
  2. 2Test database operations like login or registration
  3. 3Verify https://yourdomain.com/.env returns 404 Not Found to ensure sensitive keys are protected
  4. 4Check storage/logs/laravel.log in File Manager if any error occurs

⚠️ Common Errors & How to Fix Them

❌HTTP 500 Internal Server Error

Root Cause: Permissions on storage or bootstrap/cache folders are not writable (need 775 or 755), or PHP version mismatch.

Fix: Set permissions on storage and bootstrap/cache to 755 (or 775). In cPanel 'Select PHP Version', ensure PHP 8.2+ with required extensions (pdo_mysql, mbstring, openssl, xml) is active.

❌Vite manifest not found at: public/build/manifest.json

Root Cause: Forgot to run 'npm run build' before uploading to cPanel.

Fix: Run npm run build locally, then upload the resulting public/build directory into public_html/build.

🛡️ Security Considerations & Best Practices

  • !Never leave APP_DEBUG=true in production; it reveals stack traces and environment variables on error.
  • !Delete any temporary setup routes (like /create-storage-link or migration routes) immediately after use.

❓ Frequently Asked Questions (FAQ)

Yes, you can either export your database schema locally with phpMyAdmin and import the .sql file into cPanel phpMyAdmin, or temporarily add Route::get('/run-migrations', fn() => Artisan::call('migrate')) and delete it immediately.

📌 Summary of Key Takeaways

  • ✓Keep core Laravel code outside public_html.
  • ✓Point public_html/index.php to ../laravel_app/vendor/autoload.php.
  • ✓Set APP_DEBUG=false and secure file permissions.

Official References & Further Reading