How7o
  • Home
  • Tools
  • Prank Screens
  • Learn
  • Blog
  • Contact
Reading: Fix “403 Forbidden” on Laravel Shared Hosting
Share
How7oHow7o
Font ResizerAa
  • OS
Search
  • Home
  • Tools
  • Prank Screens
  • Learn
  • Blog
  • Contact
Follow US
© 2024–2026 How7o. All rights reserved.
How7o > Free Laravel, PHP, WordPress & Server Tutorials > Web Development > Fix “403 Forbidden” on Laravel Shared Hosting
Web Development

Fix “403 Forbidden” on Laravel Shared Hosting

how7o
By how7o
Last updated: May 10, 2026
8 Min Read
Laravel 403 forbidden on shared hosting — root htaccess rewrite into public folder
SHARE

The laravel 403 forbidden error right after uploading to shared hosting isn’t a permissions problem — it’s that Apache is serving your project root instead of the public/ folder. Laravel’s actual document root is public/, and on shared hosts the web server looks at public_html/ or www/, finds no index.html, and refuses to list the directory. The fix is either a root-level .htaccess rewrite or, if your host allows it, changing the document root to point at public/ directly.

Contents
  • TL;DR
  • Why the error happens
  • Fix 1 — Root .htaccess rewrite (quick, works on any shared host)
  • Fix 2 — Change the document root (cleaner, if your host allows)
  • After the fix — clear caches and set permissions
  • Frequently asked questions
  • Related guides
  • References

Last verified: 2026-04-23 on Laravel 11 with PHP 8.3 and Apache 2.4 on cPanel. Originally published 2022-07-06, rewritten and updated 2026-04-23.

TL;DR

Create .htaccess in the Laravel project root with the rewrite below. It forwards every request into public/, where Laravel’s own .htaccess takes over.

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_URI} !^public
    RewriteRule ^(.*)$ public/$1 [L]
</IfModule>

Why the error happens

Laravel ships with two relevant entry points:

  • artisan serve — for local dev, with PHP’s built-in server pointed at public/.
  • public/index.php — the real entry point Apache/Nginx should serve in production.

When you SFTP the whole project into public_html/, Apache treats the project root as the document root. It looks for an index.html or index.php there, finds only the artisan script and a bunch of directories, and — because shared hosts disable Options +Indexes — returns:

Forbidden
You don't have permission to access / on this server.

The permissions message is misleading — the files are readable, the server just doesn’t know which file to send.

Fix 1 — Root .htaccess rewrite (quick, works on any shared host)

Drop this file at the project root (the same folder that contains artisan, composer.json, and the public/ directory):

# .htaccess (Laravel project root)
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_URI} !^public
    RewriteRule ^(.*)$ public/$1 [L]
</IfModule>

What each line does:

  • <IfModule mod_rewrite.c> — only run the rules if Apache’s mod_rewrite is loaded. Almost always is on shared hosts, but the guard prevents a 500 if it isn’t.
  • RewriteEngine On — turn the rewriting engine on for this directory.
  • RewriteCond %{REQUEST_URI} !^public — only rewrite when the URL doesn’t already start with public. Prevents an infinite loop.
  • RewriteRule ^(.*)$ public/$1 [L] — capture whatever came in (^(.*)$) and rewrite it as public/<capture>. [L] stops further rewrites in this .htaccess.

Laravel’s stock public/.htaccess then takes the rewritten path and dispatches it to index.php. The router handles the URL from there.

laravel 403 forbidden on shared hosting — root htaccess rewrite flows requests into public

Fix 2 — Change the document root (cleaner, if your host allows)

A better long-term layout is to keep the Laravel app outside public_html/ entirely and point the hosting document root at the public/ subfolder inside it:

/home/user/
├── app/                     (Laravel project — not web-accessible)
│   ├── app/
│   ├── bootstrap/
│   ├── config/
│   ├── public/              ← document root points here
│   └── ...
└── public_html/             (empty or symlinked to ../app/public)

Most hosting panels expose this setting:

  • cPanel → Domains → Document Root → /home/user/app/public.
  • Plesk → Hosting Settings → Document Root.
  • aaPanel → Site → Site Directory (set to the public subfolder).

This keeps .env, storage/, and vendor/ outside the web-reachable tree, which is the layout Laravel is designed for.

After the fix — clear caches and set permissions

If the 403 is gone but you now see 500 errors, two usual causes:

# 1. storage/ and bootstrap/cache/ must be writable by the web user
chmod -R 775 storage bootstrap/cache

# 2. Rebuild the config/route caches after moving files
php artisan config:clear
php artisan route:clear
php artisan view:clear

Run those from SSH on the host. If SSH isn’t available, chmod via the file manager’s permissions dialog and delete bootstrap/cache/config.php manually.

Frequently asked questions

Why do I get laravel 403 forbidden after uploading to shared hosting?

Because Laravel’s actual document root is the public/ folder, but most shared-hosting accounts serve from /home/user/public_html/ or www/. Apache looks in your project root for an index.html or index.php, doesn’t find one (you didn’t upload those, you uploaded a Laravel app), and returns 403 because directory listings are disabled. The fix is an .htaccess file that rewrites all requests into public/ — or, better, pointing the host’s document root directly at the public/ folder.

What’s the cleanest fix — .htaccess rewrite or document-root change?

Changing the document root to /home/user/project/public in the hosting control panel (cPanel → Domains → Document Root, Plesk → Hosting Settings, etc.). That’s what Laravel expects and what the framework’s docs recommend. The .htaccess rewrite is a workaround for hosts that won’t let you change the document root — it works, but every request pays a rewrite cost and you have two .htaccess files (root and public/) to keep in sync.

Is it safe to put my whole Laravel app inside public_html?

Not ideally. When the app lives inside public_html, Apache can serve any file in the project tree — .env, storage/logs/, vendor/ — as long as someone guesses the URL. The .htaccess rewrite does block direct hits to the project root, but it’s safer to keep the Laravel app outside the web root and point the document root at only the public/ subfolder. On hosts where that’s impossible, at minimum make sure .env is protected (most default Apache configs already block hidden files).

Why does RewriteCond %{REQUEST_URI} !^public need that exact form?

The condition says “only apply this rewrite when the request URI does NOT start with public.” Without it, the rewrite would re-fire on the already-rewritten request (/foo → public/foo → public/public/foo → …) and either loop or 500 out. The leading ! is the negation; ^public is a regex for “starts with public”.

Will this break the Laravel public/.htaccess that’s already there?

No — the two files work in sequence. Your new root-level .htaccess rewrites the request URL into public/. Laravel’s stock public/.htaccess then handles the further rewrite into index.php?... so the router can match it. Keep both files; they do different jobs.

Related guides

  • How to Exclude .well-known from Redirection for Let’s Encrypt with .htaccess in Laravel — the companion rule that lets ACME challenges reach /.well-known/.
  • How to Install Laravel on Ubuntu — a fresh Laravel 11 install with the correct folder layout.
  • How to Run a Laravel Project from GitHub — the clone + deploy flow that lands you on shared hosting.
  • How to Fix cURL Error 60 SSL Certificate Problem in Laravel — another post-deploy-to-shared-hosting problem worth knowing.

References

Apache mod_rewrite docs: httpd.apache.org/docs/current/mod/mod_rewrite. Laravel deployment: laravel.com/docs/deployment.

TAGGED:ApachecPanelLaravelphptroubleshooting

Sign Up For Daily Newsletter

Be keep up! Get the latest breaking news delivered straight to your inbox.
[mc4wp_form]
By signing up, you agree to our Terms of Use and acknowledge the data practices in our Privacy Policy. You may unsubscribe at any time.
Share This Article
Facebook Copy Link Print
Previous Article Laravel global variable for views — View::share in AppServiceProvider and View::composer wildcard patterns How to Set a Global Variable for Laravel Views
Next Article Laravel cURL error 60 SSL certificate problem — CA bundle wiring in php.ini How to Fix cURL Error 60 SSL Certificate Problem in Laravel
Leave a Comment

Leave a Reply Cancel reply

You must be logged in to post a comment.

FacebookLike
XFollow
PinterestPin
InstagramFollow
Most Popular
Set vi as the default editor in Ubuntu — a terminal opening the vim editor
How to Set vi (Vim) as the Default Editor in Ubuntu
June 8, 2026
rsync says ALL DONE but files are missing — a terminal showing ALL DONE next to an empty folder
rsync Says “ALL DONE” but Files Are Missing: How to Verify
June 8, 2026
Migrate a website to a new server with rsync — files copying from an old server to a new one over SSH
How to Migrate a Website to a New Server With rsync
June 8, 2026
Bun runtime — faster JS toolkit replacing npm in Laravel projects
How to Install Bun Runtime on Ubuntu (And Use It in a Laravel Project)
May 24, 2026
Tailscale mesh — peer-to-peer connections between devices, coordination server
How to Install Tailscale on Ubuntu (Zero-Config Mesh VPN for Self-Hosters)
May 24, 2026

You Might Also Like

WooCommerce My Account login form hooks — five positions for injecting content
Web Development

How to Add a Link or Button After the Login Form in WooCommerce

6 Min Read
Open a file selection dialog from a button click
Web Development

How to Open a File Dialog When Clicking a Button

4 Min Read
Check if GD library is installed in PHP (phpinfo and extension_loaded)
Web Development

How to Check if GD Library Is Installed in PHP (3 Easy Methods)

5 Min Read
Laravel migration column types cheat sheet
Web Development

Laravel Migration Column Types — Cheat Sheet

5 Min Read
How7o

We provide tips, tricks, and advice for improving websites and doing better search.

Tools

  • Age Calculator
  • Word Counter
  • Image Upscaler
  • Password Generator
  • QR Code Generator
  • See all tools→

Pranks

  • Fake Blue Screen Prank
  • Hacker Typer
  • Fake iMessage Generator
  • Windows XP Crash Prank
  • Windows 11 Update Prank
  • See all prank screens →

Company

  • About Us
  • Blog
  • Contact
  • Privacy Policy
  • Terms of Service
  • Sitemap
© 2024–2026 How7o. All rights reserved.
Welcome Back!

Sign in to your account

Username or Email Address
Password

Lost your password?