Posted Sep 29, 2026 · 5 min read · 1 view
Deploying a Laravel App on Shared cPanel Hosting, Step by Step
Not every project gets a VPS. Many clients in Bangladesh run on shared cPanel hosting, and Laravel does not deploy there the way it does on a modern server. There is no public folder as the web root, SSH is sometimes missing, and symlinks may be blocked.
The good news is that Laravel runs perfectly well on cPanel once you set it up correctly. This guide walks through a safe, repeatable deployment.
What you need before you start
- A cPanel hosting account with PHP 8.2 or newer available
- Your Laravel project on your computer
- A MySQL database created in cPanel
- Optional but helpful: SSH or Terminal access in cPanel
Check your PHP version first. In cPanel, open MultiPHP Manager and select the PHP version your Laravel release requires. Also confirm that extensions like mbstring, openssl, pdo_mysql, tokenizer, xml, ctype, json, and fileinfo are enabled under Select PHP Version.
Step 1: Prepare the project locally
Install production dependencies and build your assets before uploading:
composer install --optimize-autoloader --no-dev
npm run build
Now zip the project. Exclude node_modules, .git, and your local .env file. Uploading one zip is much faster and safer than uploading thousands of small files through FTP.
Step 2: Choose the right folder structure
This is the step most people get wrong. Do not put your whole Laravel project inside public_html. That would expose your .env, source code, and config files to the internet.
Use this structure instead:
/home/username/
laravel-app/ <- everything except the public folder
public_html/ <- contents of Laravel's public folder
Upload and extract your zip into /home/username/laravel-app. Then move the contents of laravel-app/public into public_html.
If you are deploying to a subdomain, replace public_html with the subdomain's document root, for example public_html/app or app.example.com.
Step 3: Fix the paths in index.php
Since public_html and laravel-app are now siblings, Laravel needs to know where to find the framework. Open public_html/index.php and update these lines:
require __DIR__.'/../laravel-app/vendor/autoload.php';
$app = require_once __DIR__.'/../laravel-app/bootstrap/app.php';
Also check the maintenance mode line near the top and point it to the new location:
if (file_exists($maintenance = __DIR__.'/../laravel-app/storage/framework/maintenance.php')) {
require $maintenance;
}
Step 4: Configure the environment file
Create a .env file inside laravel-app and set production values:
APP_NAME="My App"
APP_ENV=production
APP_DEBUG=false
APP_URL=https://example.com
DB_CONNECTION=mysql
DB_HOST=localhost
DB_DATABASE=cpanelusername_dbname
DB_USERNAME=cpanelusername_dbuser
DB_PASSWORD=your-strong-password
Two things to remember. First, APP_DEBUG must be false in production, otherwise errors expose secrets. Second, cPanel prefixes database and user names with your account name, so copy the full names exactly as cPanel shows them.
Generate an app key if you have not already:
php artisan key:generate
No SSH? Run it locally and paste the generated APP_KEY into your server's .env.
Step 5: Run migrations
With SSH or the cPanel Terminal, this is easy:
cd ~/laravel-app
php artisan migrate --force
Without terminal access, you have two options. You can export your local database and import the SQL file through phpMyAdmin, or you can create a temporary protected route that calls Artisan::call('migrate', ['--force' => true]). If you use the route approach, delete it immediately after use.
Step 6: Set permissions and storage
Laravel needs to write to storage and bootstrap/cache. Set folders to 755 and make sure your account owns them:
chmod -R 755 storage bootstrap/cache
Only use 775 if your host requires it. Never use 777.
Next, the storage link. The usual command is:
php artisan storage:link
On many shared hosts this fails because symlinks are disabled. In that case, create the link with a small PHP script or use a cPanel cron job that runs ln -s. A simple alternative is to change your filesystem disk in config/filesystems.php so the public disk points directly to a folder inside public_html.
Step 7: Cache for production
Once everything works, speed it up:
php artisan config:cache
php artisan route:cache
php artisan view:cache
Remember to clear and rebuild these caches after every deployment that changes config or routes.
Step 8: Add a cron job for the scheduler
If your app uses scheduled tasks or queues, add this in Cron Jobs in cPanel, set to run every minute:
* * * * * /usr/local/bin/php /home/username/laravel-app/artisan schedule:run >> /dev/null 2>&1
The PHP path may differ on your host, so confirm it with your provider.
Common problems and fixes
| Problem | Likely cause | Fix |
|---|---|---|
| 500 error on every page | Wrong paths in index.php or missing .env | Recheck Step 3 and Step 4 |
| Blank white page | Missing PHP extension or wrong PHP version | Check MultiPHP Manager |
| Images not loading | Storage link missing | Redo the storage link fix |
| Routes return 404 | Missing .htaccess in public_html | Copy it from Laravel's public folder |
| Permission denied errors | Wrong storage permissions | Set 755 and check ownership |
When something fails, open laravel-app/storage/logs/laravel.log. It almost always tells you the exact problem.
Security checklist
Before you announce the site is live, verify these:
APP_DEBUG=falseandAPP_ENV=production.envis outsidepublic_html- SSL is enabled and HTTP redirects to HTTPS
- Directory listing is disabled
- Database password is strong and unique
Final thoughts
Shared hosting is not glamorous, but it is where a lot of real projects live. If you keep the app outside the web root, fix the paths in index.php, and handle storage and permissions carefully, Laravel runs smoothly on cPanel. Save these steps as a checklist and your next deployment will take minutes instead of hours.
Deploying on a tricky host? Share your setup in the comments and let us figure it out together.
Discussion (0)