Serve a Laravel application with Apache on Debian 13
verified on 7 September 2026 · 4 min
Three conditions to serve Laravel with Apache: DocumentRoot on the public/ directory, sudo a2enmod rewrite, and AllowOverride All on that directory. Without the last directive, Apache ignores the .htaccess shipped by Laravel: the home page shows up but every other route returns 404.
Apache is still the most widespread web server on shared hosting and on servers that have been running for years. Serving a Laravel application behind Apache takes three things: a virtual host whose document root points at public/, the rewrite module enabled, and AllowOverride All so that the .htaccess file shipped by Laravel is actually read.
This chapter belongs to installing Laravel on a Debian server, part of the Web development path. Missing that third point is the near-systematic reason routes come back as 404. Steps verified on Debian 13 with Apache 2.4.68 and Laravel 13.30.1.
Install Apache and the PHP module
sudo apt update
sudo apt install -y apache2 libapache2-mod-php
apache2 -vServer version: Apache/2.4.68 (Debian)The libapache2-mod-php package enables PHP inside Apache. Check it:
a2query -m | grep phpphp8.4 (enabled by maintainer script)Then enable the rewrite module, which is not active by default:
sudo a2enmod rewritePut the project in place
Create the application with Composer, under an unprivileged account:
su - deploy
composer create-project laravel/laravel /home/deploy/app --no-interaction
cd /home/deploy/app
php artisan --version # Laravel Framework 13.30.1Then give write permissions to the only three folders that need them, database/ included as long as the application uses the SQLite database created at install time:
sudo chown -R deploy:www-data /home/deploy/app/storage /home/deploy/app/bootstrap/cache /home/deploy/app/database
sudo chmod -R 775 /home/deploy/app/storage /home/deploy/app/bootstrap/cache /home/deploy/app/database
sudo chmod o+x /home/deploy # Apache must be able to traverse the home directoryCreate the virtual host
<VirtualHost *:80>
ServerName exemple.com
DocumentRoot /home/deploy/app/public
<Directory /home/deploy/app/public>
AllowOverride All
Require all granted
Options -Indexes +FollowSymLinks
</Directory>
ErrorLog ${APACHE_LOG_DIR}/laravel-error.log
CustomLog ${APACHE_LOG_DIR}/laravel-access.log combined
</VirtualHost>Enable the site, disable the default one, check the syntax:
sudo a2ensite laravel
sudo a2dissite 000-default
sudo apache2ctl configtest
sudo systemctl reload apache2Syntax OKAllowOverride All: the line that decides everything
Laravel ships a public/.htaccess file that sends every request to index.php. Apache only reads that file if the AllowOverride directive lets it. Without that directive the home page still shows up, but no other route answers.
The behaviour was measured on the /up health route, present in every recent installation:
| Configuration | / | /up |
|---|---|---|
AllowOverride All and rewrite enabled | 200 | 200 |
AllowOverride None | 200 | 404 |
That is the symptom to recognise: the home page works, everything else returns 404. The problem is neither in the routes nor in the code, but in the Apache configuration.
The beginning of the .htaccess shipped by Laravel, which this whole mechanism depends on:
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes
</IfModule>
RewriteEngine On
# Handle X-XSRF-Token Header
RewriteCond %{HTTP:x-xsrf-token} .
RewriteRule .* - [E=HTTP_X_XSRF_TOKEN:%{HTTP:X-XSRF-Token}]
# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]Check the installation
curl -s -o /dev/null -w "accueil : %{http_code}\n" http://127.0.0.1/
curl -s -o /dev/null -w "route : %{http_code}\n" http://127.0.0.1/up
curl -s -o /dev/null -w ".env : %{http_code}\n" http://127.0.0.1/.envaccueil : 200
route : 200
.env : 404The 404 on /.env is the expected result: since the site root is public/, the file sits outside the served tree and is simply not reachable. If you get a 200, then DocumentRoot points at the project root instead of public/, and your database credentials are readable by anyone.
Move to HTTPS
Certbot edits the virtual host itself and sets up automatic renewal:
sudo apt install -y certbot python3-certbot-apache
sudo certbot --apache -d exemple.comRemember to turn debug mode off and to line APP_URL up with the HTTPS address, otherwise the links and assets generated by Laravel will carry on pointing at HTTP:
APP_ENV=production
APP_DEBUG=false
APP_URL=https://exemple.comphp artisan config:cache
php artisan route:cache
php artisan view:cacheApache or nginx
| Apache | nginx | |
|---|---|---|
| Running PHP | built-in module or PHP-FPM | PHP-FPM only |
| Per-directory configuration | yes, through .htaccess | no, everything in the site configuration |
| Laravel rewriting | shipped with Laravel | to be written in the location block |
| Shared hosting | very common | rare |
The .htaccess file is Apache’s decisive advantage on shared hosting: it lets you set up rewriting without access to the server configuration. On a machine you administer yourself, either one will do, and the choice comes down to what you know how to run.
Common errors
chmod o+x /home/deploy, otherwise Apache returns a permission error.