Two of the most common headaches when deploying to Apache shared hosting are Angular routes that return 404 on refresh and Laravel apps that won’t run because the host serves from public_html instead of Laravel’s public/ folder. Both are fixed with the right .htaccess configuration. This guide gives you working files for each, and explains why they work so you can adapt them to your setup.
Why these problems happen
Apache’s default behavior is to map a URL directly to a file or directory on disk. Single-page apps (Angular) and front-controller frameworks (Laravel) both break that assumption: they want every request routed to a single entry point so their own router can take over. On shared hosting you usually can’t edit the main Apache config, but you can drop an .htaccess file in your web root — and that’s enough to fix both cases.
Fix 1 — Angular routing 404 on Apache shared hosting
Angular uses the HTML5 history API for clean URLs like /dashboard or /products/42. These work fine while navigating inside the app, because Angular handles them in the browser. But when a user refreshes or opens a deep link directly, the browser asks Apache for /products/42 — and since no such file exists on disk, Apache returns 404.
The fix: tell Apache that if the requested path isn’t a real file or directory, serve index.html and let Angular’s router handle it. Put this .htaccess in the same folder as your built index.html (usually the contents of dist/your-app/):
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
# Don't rewrite real files or directories
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# Everything else -> index.html (Angular router takes over)
RewriteRule ^ index.html [L]
</IfModule>
The two RewriteCond lines are the important part: -f means “is a real file” and -d means “is a real directory.” If either is true (your JS bundles, CSS, images), Apache serves the file untouched. Otherwise the final rule rewrites the request to index.html. If your app is deployed in a subfolder (for example https://example.com/app/), set RewriteBase /app/ and build Angular with --base-href /app/.
Fix 2 — Laravel and the public/ vs public_html problem
Laravel is designed so that only its public/ directory is web-accessible; everything else (your .env, source code, vendor libraries) sits above the web root for security. But shared hosting serves your site from a fixed folder called public_html, and you often can’t change which directory the domain points to. So how do you reconcile Laravel’s public/ with the host’s public_html? You have three options, best to worst.
Option A (best): point the domain’s document root at public/
If your control panel lets you set the document root for the domain or a subdomain (cPanel often does, especially for addon/subdomains), point it directly at .../your-laravel-app/public. Upload the entire Laravel app outside public_html and set the docroot to its public folder. This keeps Laravel’s intended security model intact and needs no path hacks.
Option B: serve from public_html and patch index.php
If you can’t change the document root, move the contents of Laravel’s public/ into public_html, and place the rest of the app in a sibling folder (for example ~/laravel-app, outside public_html). Then edit public_html/index.php so it points at the relocated framework:
// public_html/index.php
require __DIR__.'/../laravel-app/vendor/autoload.php';
$app = require_once __DIR__.'/../laravel-app/bootstrap/app.php';
Adjust the relative paths (../laravel-app/) to match where you put the app. Keep the rest of index.php as-is.
Option C (last resort): redirect public_html into public/
If you must keep the whole app inside public_html, drop this .htaccess in public_html to internally rewrite all traffic into the public subfolder:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^(.*)$ public/$1 [L]
</IfModule>
This works but is the least clean: your application code is technically reachable under the web root, so make sure Laravel’s own public/.htaccess (below) is intact and that sensitive files are protected.
Laravel’s standard public/.htaccess
Whichever option you choose, Laravel’s front controller relies on its bundled public/.htaccess. If it went missing, recreate it:
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes
</IfModule>
RewriteEngine On
# Handle Authorization header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Redirect trailing slashes
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
# Send all requests to index.php
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>
Quick comparison
| Scenario | Where the .htaccess goes | What it does |
|---|---|---|
| Angular SPA | Next to index.html | Routes unknown paths to index.html |
| Laravel — Option A | (none needed) | Docroot points at public/ |
| Laravel — Option C | public_html/.htaccess | Rewrites traffic into public/ |
Troubleshooting
- 500 Internal Server Error after adding .htaccess. Usually a syntax error or
mod_rewriteisn’t enabled. Check the host’s error log; most shared hosts havemod_rewriteon by default. - Angular still 404s on refresh. The
.htaccessisn’t in the same folder asindex.html, orRewriteBasedoesn’t match your subfolder. - Laravel shows the directory listing or a blank page. The document root isn’t pointing where you think, or
index.phppaths are wrong. Verify with aphpinfo()test or the host’s file manager. - CSS/JS 404 in Angular. You built without the correct
--base-hreffor a subfolder deployment.
Deploying from a Git workflow? Pair this with our guides on deploying Angular to Hostinger with Bitbucket Pipelines and CI/CD with Bitbucket Pipelines for Laravel, and make sure you can connect over SSH first.
Frequently asked questions
Why does my Angular app return 404 on refresh on shared hosting?
Because Apache looks for a real file matching the URL and doesn’t find one. Add an .htaccess that rewrites any non-file, non-directory request to index.html so Angular’s router can handle it.
How do I run Laravel when hosting serves from public_html?
Best: point the domain’s document root at Laravel’s public/ folder. If you can’t, move the contents of public/ into public_html, keep the rest of the app outside it, and update the paths in index.php. As a last resort, add an .htaccess in public_html that rewrites requests into a public/ subfolder.
Do I need mod_rewrite for these fixes?
Yes. Both fixes rely on Apache’s mod_rewrite module, which is enabled by default on virtually all shared hosting.