How to fix Laravel's "500 Server Error" on your own server
In production, Laravel hides the details and shows only "500 Server Error". Most of the time the cause is one of a few things: a folder the web server can't write, a missing key or old cached settings. This guide shows how to find which one.
deploy, and PHP ran as www-data, a common setup. We broke the app one way at a time and copied the real messages. Ubuntu works the same way.1. Read Laravel's log, and check you can
tail -n 50 /var/www/app/storage/logs/laravel.log
Look for the newest line with production.ERROR. The message after it is the real reason.
If the log has nothing new, check who owns it:
ls -l /var/www/app/storage/logs/
If laravel.log belongs to root (or another user) with -rw-r--r--, the web server can't write to it. This happens easily: someone runs sudo php artisan ..., something goes wrong, and Laravel creates the log file as root. In our test, the site kept working. But when a real error came, it was lost. It wasn't in laravel.log, the Nginx log or the PHP-FPM log. You get a 500 and no trace at all. Fix the permissions (step 2), then load the page again to see the real error.
Don't turn on APP_DEBUG=true on a live site to see the error. It shows the error with code and settings to every visitor. Read the log instead.
2. Folders the web server must be able to write
Laravel writes to storage/ (logs, sessions, compiled pages, cache) and bootstrap/cache/. If PHP can't write there, you get a 500. In our tests:
storage/framework/viewsnot writable: 500, and in the logtempnam(): file created in the system's temporary directory. Laravel couldn't save the compiled page.bootstrap/cachenot writable: an empty 500 page, and in the logThe /var/www/app/bootstrap/cache directory must be present and writable.- SQLite database not writable: a brand-new Laravel 13 app gave a 500 on its first page:
attempt to write a readonly database. New apps keep sessions indatabase/database.sqlite, and the web server can't write to that file or its folder. If you use MySQL or MariaDB, this doesn't apply.
The fix that lasted in our test: ACLs. Plain owner and group settings break again as soon as another user creates a new file. In our test, the deploy user ran an artisan command, which created a new log file that www-data couldn't write. ACLs give both users write rights, including on files created later:
sudo apt install acl
cd /var/www/app
sudo setfacl -R -m u:www-data:rwX -m u:deploy:rwX storage bootstrap/cache
sudo setfacl -dR -m u:www-data:rwX -m u:deploy:rwX storage bootstrap/cache
The first setfacl fixes the files there now. The second (-d) sets the default for new files. Change deploy to the user that owns your code. If each site has its own PHP user, use that user instead of www-data. In our test, new log files stayed writable for www-data, even when deploy or root created them.
For SQLite, the database file and its folder must be writable, because SQLite creates extra files next to it:
sudo setfacl -m u:www-data:rwX database database/database.sqlite
Don't give the web server write rights to the rest of the code. If a weakness lets an attacker write files, they shouldn't be able to change your PHP code. And never use chmod -R 777.
Better habit: run artisan as the user that owns the code, not as root: sudo -u deploy php artisan migrate.
3. "No application encryption key has been specified."
In our test, an empty APP_KEY= in .env gave a 500, and the log said No application encryption key has been specified. (MissingAppKeyException). This usually happens on a new server, when .env was copied from .env.example.
- Moving an existing app? Copy the
APP_KEYfrom the old server. Don't make a new one: a new key logs everyone out and makes anything Laravel encrypted unreadable. - A brand-new app? Make one:
sudo -u deploy php artisan key:generate.
4. You changed .env, and nothing happened
Many deploy scripts run php artisan config:cache. It saves all settings into bootstrap/cache/config.php, and from then on Laravel doesn't read .env at all. In our test, we cached the settings, then changed APP_DEBUG in .env. The site kept the old value until we cleared the cache.
After every change to .env, build the cache again (or clear it):
sudo -u deploy php artisan config:cache
If an old cache points at paths or settings that no longer exist, for example after moving the app to a new folder, clear everything once:
sudo -u deploy php artisan optimize:clear
5. Other causes worth checking
- Missing
vendorfolder or packages: runcomposer install --no-devafter each deploy. See keeping Composer packages safe. - Database not reachable: check
DB_HOST,DB_DATABASEand the password in.env. Test the login as shown in our database connection guide. The steps are the same for Laravel. - Missing PHP extension: the log names the function or class. Compare with
php-fpm8.4 -m, notphp -m, because the website uses PHP-FPM. - Out of memory: see fixing "Allowed memory size exhausted".
- A 502 from Nginx instead of a 500: PHP itself isn't answering. See fixing 502 and 504 errors.
Quick list
- Start here:
tail storage/logs/laravel.log. If it has nothing new, check thatwww-datacan write the file. - tempnam() / bootstrap/cache must be writable / readonly database: give the web server write rights on
storage,bootstrap/cache(and the SQLite folder) withsetfacl. - No application encryption key: copy
APP_KEYfrom the old server, or runkey:generateon a new app only. - .env change ignored: run
php artisan config:cacheagain. - Never use
APP_DEBUG=trueon a live site, orchmod 777. - Run artisan as the code's owner, not as root.
Official sources
- Laravel 13: deployment (folder permissions, caching settings)
- Laravel 13: configuration (
APP_KEY,APP_DEBUG,config:cache) - Laravel 13: logging
- setfacl manual (Debian 13)
Next steps: see deploying Laravel on your own server and zero-downtime deploys, which set up these folders once for every release.
ServerNest is being built to set up Laravel apps with the right users and permissions, and to show their errors in one place. Join early access to try it first.