Laravel 500 Internal Server Error: Every Fix a Developer Needs
You deploy your Laravel app, open it in the browser, and all you see is a blank white page with "500 Internal Server Error." No stack trace. No hint. Just a wall of nothing. This is one of the most frustrating experiences in web development because Laravel is actively trying to tell you what went wrong, but you cannot see it yet. Let us fix that.
TL;DR: Start Here
1. Check the log file first: tail -50 storage/logs/laravel.log
2. If no log file exists, it is a permissions problem. Run: chmod -R 775 storage bootstrap/cache
3. Clear all caches: php artisan config:clear && php artisan cache:clear && php artisan route:clear && php artisan view:clear
4. If you just deployed fresh, generate an app key: php artisan key:generate
5. Still broken? Read on. We cover every cause below.
Why Laravel Shows a Generic 500 Error
Before we start fixing things, it helps to understand why Laravel is being so unhelpful. The answer is simple: APP_DEBUG=false.
When your .env file has APP_DEBUG=false (which it should in production), Laravel catches every exception and returns a generic 500 page instead of dumping a full stack trace into the browser. This is a security feature. You do not want the entire world seeing your database credentials, file paths, and internal class names when something breaks.
The real error message is not gone. It is sitting in storage/logs/laravel.log, waiting for you to read it. Every fix in this guide starts with reading that log file first, because guessing at the problem is a waste of time when Laravel already wrote down the answer.
That said, there are situations where the error happens so early in the boot process that Laravel cannot even write to the log file. A missing .env file, broken permissions on the storage directory, or a fatal PHP error can all prevent logging from working. In those cases, you need to check your web server's error log instead (usually /var/log/apache2/error.log or /var/log/nginx/error.log).
Step 1: Read the Log File
This is the single most important step. Nine times out of ten, the log file tells you exactly what is wrong.
tail -50 storage/logs/laravel.log
Look at the most recent entry. Laravel logs include a timestamp, the error level, and a full stack trace. Common things you will see:
No application encryption key has been specified- Jump to Step 4SQLSTATE[HY000] [2002] Connection refused- Jump to Step 7The stream or file "storage/logs/laravel.log" could not be opened in append mode- Jump to Step 3Class "App\Http\Controllers\SomeController" not found- Jump to Step 5failed to open stream: No such file or directoryfor a config file - Jump to Step 6
If the log file does not exist at all, or it exists but has no recent entries, the problem is happening before Laravel can initialize its logging. Move to Step 2.
If you do not have SSH access, many hosting panels let you view files through a browser-based file manager. Navigate to storage/logs/ and open the most recent log file.
Step 2: .env File Missing or Corrupt
Laravel depends on the .env file for almost everything: database credentials, the app key, cache drivers, mail configuration, and more. If this file is missing, Laravel cannot boot at all.
This happens more often than you would think. The .env file starts with a dot, which makes it a hidden file on Linux and macOS. If you copied your project files with a drag-and-drop file manager, there is a good chance it was silently excluded. Git also ignores it by default (and it should, because it contains secrets).
Check if it exists:
ls -la .env
If it is missing, create it from the example file:
cp .env.example .env
php artisan key:generate
Then update the database credentials and any other environment-specific values. Pay attention to these critical fields:
APP_NAME=YourApp
APP_ENV=production
APP_KEY= # Generated by key:generate
APP_DEBUG=false
APP_URL=https://yourdomain.com
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=your_database
DB_USERNAME=your_user
DB_PASSWORD=your_password
A subtle gotcha: if your .env file has a syntax error (like a password with unescaped special characters or a missing closing quote), Laravel will silently fail to parse it. If your database password contains characters like #, ", or spaces, wrap the entire value in double quotes:
DB_PASSWORD="my#complex password"
Security note: Your
.envfile contains database passwords, API keys, and encryption secrets. If it is publicly accessible through your web server, anyone can steal your credentials. Read our guide on the danger of exposed .env files to make sure yours is locked down.
Step 3: Storage and Bootstrap Permissions
Laravel needs to write to two directories: storage/ (for logs, compiled views, cache files, and sessions) and bootstrap/cache/ (for the framework bootstrap cache). If your web server user cannot write to these directories, you get a 500 error.
This is the most common cause of 500 errors right after deployment. You pulled the code from Git, ran composer install, but the directory permissions are wrong because your deployment user is different from your web server user (usually www-data on Ubuntu or apache on CentOS).
The fix:
# Set the correct ownership
sudo chown -R www-data:www-data storage bootstrap/cache
# Set directory permissions
sudo chmod -R 775 storage bootstrap/cache
Make sure the subdirectories exist too. Laravel expects this directory structure inside storage/:
storage/
app/
public/
framework/
cache/
data/
sessions/
views/
logs/
If any of these are missing (which can happen if you ran git clone on a repo that has storage/ in the .gitignore), create them:
mkdir -p storage/framework/{cache/data,sessions,views}
mkdir -p storage/app/public
mkdir -p storage/logs
Then set permissions again after creating them.
A common mistake on shared hosting is using chmod 777. This technically works, but it means every user on the server can read, write, and execute files in your storage directory. That is a security risk. Stick with 775 and set the correct owner/group instead.
Step 4: APP_KEY Not Set
Laravel uses the APP_KEY for encrypting cookies, sessions, CSRF tokens, and any data you encrypt with the Crypt facade. Without it, Laravel refuses to start.
The error message in the log will say:
No application encryption key has been specified.
Generate one:
php artisan key:generate
This writes a base64: encoded 32-byte key to the APP_KEY field in your .env file. If you have already cached your config (more on that in Step 6), you need to clear it first:
php artisan config:clear
php artisan key:generate
Important: Never change the APP_KEY on an existing production application unless you know what you are doing. Changing it will invalidate every encrypted value in your database, all active user sessions, and all signed URLs. Users will be logged out, and encrypted data will become unreadable. Only generate a new key on fresh deployments or when rotating after a suspected compromise.
Step 5: Composer Autoload Out of Sync
PHP uses Composer's autoloader to find classes. If you added a new class, renamed a file, changed a namespace, or pulled code that includes new classes, and the autoload map is stale, PHP will throw a "Class not found" fatal error that manifests as a 500.
The fix is simple:
composer dump-autoload
For production, use the optimized autoloader for better performance:
composer dump-autoload --optimize
If you are seeing 500 errors after pulling new code, you might also need to install new dependencies that were added:
composer install --no-dev --optimize-autoloader
The --no-dev flag skips development dependencies (like PHPUnit and debug bars) that do not belong in production. The --optimize-autoloader flag builds a class map for faster autoloading.
One thing to watch out for: if your hosting environment does not have enough memory for Composer, the install process itself can fail silently and leave you with a broken vendor/ directory. Check that the installation completed successfully by verifying the vendor/autoload.php file exists.
Step 6: Stale Cached Configuration
Laravel lets you cache your configuration, routes, and views for better performance. This is great in production, but it causes problems when the cached versions do not match your current code or .env values.
This is a very common cause of 500 errors after deployment. You updated your .env file with new database credentials, but Laravel is still reading the old cached credentials. Or you changed a route, but the cached route file still has the old one.
Clear everything:
php artisan config:clear
php artisan route:clear
php artisan view:clear
php artisan cache:clear
Or use the nuclear option that clears all of them at once:
php artisan optimize:clear
After clearing, you can optionally rebuild the caches for production:
php artisan config:cache
php artisan route:cache
php artisan view:cache
A subtle trap: if you run php artisan config:cache from the command line, it bakes the current environment variables into the cache file. If your web server runs under a different user or has different environment variables, the cached config will have wrong values. Always run artisan commands as the same user that serves your application:
sudo -u www-data php artisan config:cache
Step 7: Database Connection Failed
If Laravel boots successfully but cannot reach the database, every page that touches the database (which is most of them) will throw a 500 error. The log file will show something like:
SQLSTATE[HY000] [2002] Connection refused
SQLSTATE[HY000] [1045] Access denied for user 'forge'@'localhost'
SQLSTATE[HY000] [2002] No such file or directory
Each of these means something different:
- Connection refused - The database server is not running, or the host/port is wrong
- Access denied - The username or password in your
.envis incorrect - No such file or directory - Laravel is trying to connect via a Unix socket that does not exist. Change
DB_HOSTfromlocalhostto127.0.0.1
Test the connection from the command line to isolate the problem:
# Test MySQL connection directly
mysql -u your_user -p -h 127.0.0.1 your_database
# Or use artisan tinker
php artisan tinker
>>> DB::connection()->getPdo();
If the MySQL command works but Laravel cannot connect, the issue is in your .env or cached config. Clear the config cache and double-check every database field. Also verify that you do not have a DB_SOCKET variable overriding the host connection.
On shared hosting, the database host might not be 127.0.0.1. Check your hosting control panel for the correct hostname. It could be something like mysql.yourhostingprovider.com or an internal IP address.
Step 8: PHP Version or Extension Mismatch
Laravel has strict PHP version requirements. Laravel 10 needs PHP 8.1 or higher. Laravel 11 requires PHP 8.2 or higher. If your server runs an older PHP version, you will get a 500 error with no useful output because the error happens before Laravel can even initialize.
Check your PHP version:
php -v
But be careful: the command-line PHP version might differ from the one your web server uses. Check the web server version by creating a temporary phpinfo.php file:
<?php phpinfo(); ?>
Access it in your browser, note the version, then delete the file immediately. A phpinfo.php file left on a production server is a significant security risk because it reveals your entire server configuration to anyone who finds it.
Laravel also requires specific PHP extensions. The required extensions for most Laravel applications are:
php-mbstring- Multibyte string handlingphp-xml- XML parsing (required by many packages)php-bcmath- Arbitrary precision mathphp-curl- HTTP clientphp-zip- Archive handlingphp-json- JSON support (built-in since PHP 8.0)php-tokenizer- Required for Blade templatesphp-fileinfo- MIME type detection
Check which extensions are installed:
php -m
Install missing ones (Ubuntu/Debian example):
sudo apt install php8.2-mbstring php8.2-xml php8.2-bcmath php8.2-curl php8.2-zip
APP_DEBUG=true temporarily on your development or staging environment to see the full error in the browser. This makes debugging dramatically faster because you get the stack trace, the file, and the line number right on the screen. But never leave APP_DEBUG=true in production. It exposes your database credentials, .env variables, full file paths, and source code to anyone who triggers an error. This is not a theoretical risk. There are automated scanners that specifically look for Laravel debug pages to harvest credentials. If your .env is accidentally exposed, learn why that is dangerous in our guide on exposed .env files.
Laravel Error Codes and What They Mean
The 500 error is not the only HTTP status code Laravel throws. Here is a quick reference for the most common ones so you know where to look:
| Status Code | Meaning | Common Cause in Laravel | Quick Fix |
|---|---|---|---|
| 500 | Internal Server Error | Unhandled exception, missing .env, bad permissions, missing APP_KEY | Check storage/logs/laravel.log |
| 419 | Page Expired | CSRF token mismatch or expired session | Add @csrf to forms, check session driver |
| 403 | Forbidden | Authorization policy denied, middleware blocking access | Check Gates/Policies, route middleware. See our CORS preflight fix guide if API-related |
| 404 | Not Found | Route not defined, wrong URL, missing controller method | Run php artisan route:list to verify routes |
| 503 | Service Unavailable | Application is in maintenance mode | Run php artisan up |
The Laravel Deployment Checklist
Most 500 errors happen right after deployment. Use this checklist every time you deploy to prevent them:
- Verify
.envfile exists and has correct values for the target environment - Run
composer install --no-dev --optimize-autoloader - Run
php artisan key:generate(only on first deploy, never on existing apps) - Set permissions:
chmod -R 775 storage bootstrap/cache - Set ownership:
chown -R www-data:www-data storage bootstrap/cache - Run
php artisan migrate --force(review pending migrations first) - Run
php artisan config:cacheas the web server user - Run
php artisan route:cache - Run
php artisan view:cache - Verify the site loads and check
storage/logs/laravel.logfor any errors
Common Mistakes That Cause 500 Errors
After years of deploying Laravel applications, these are the mistakes I see over and over again:
1. Editing .env But Not Clearing Config Cache
If you ran php artisan config:cache during deployment (which you should), your .env changes will not take effect until you clear and rebuild the cache. This is the number one "it works on my machine" problem.
2. Running Artisan Commands as Root
When you run sudo php artisan config:cache, the generated cache file is owned by root. Your web server (running as www-data) cannot read it. Always run artisan as the web server user: sudo -u www-data php artisan config:cache.
3. Forgetting to Run Migrations
You pulled new code that references a database table or column that does not exist yet. The migration is there, but nobody ran it. The fix: php artisan migrate. Always check for pending migrations after pulling code.
4. Deploying Without vendor/ Directory
If your deployment process copies files but skips the vendor/ directory (or you forgot to run composer install), Laravel has no dependencies and cannot boot. The web server error log will show a fatal error about missing autoload.php.
5. Using php artisan serve in Production
The built-in development server (php artisan serve) is not meant for production. It handles one request at a time, has no process management, and will crash under load. Use Nginx or Apache with PHP-FPM for production deployments.
6. Mismatched PHP Versions Between CLI and Web Server
Your command line might run PHP 8.2 while Apache is configured to use PHP 7.4. Composer installs dependencies compatible with the CLI version, but the web server chokes on syntax it does not support. Always verify that php -v matches what the web server reports.
Is Your .env File Exposed?
A misconfigured Laravel deployment can accidentally serve your .env file to the public, leaking database passwords and API keys. Run a free check now.
Frequently Asked Questions
Why does Laravel show a 500 error instead of the actual error message?
When APP_DEBUG is set to false in your .env file (which it should be in production), Laravel intentionally hides detailed error messages to prevent leaking sensitive information like file paths, database credentials, and stack traces to end users. The real error is always written to storage/logs/laravel.log. Set APP_DEBUG=true temporarily in development to see the full error in the browser, but never leave it enabled in production.
I ran php artisan key:generate but still get a 500 error. What else should I try?
After generating the key, you need to clear all cached configuration. Run php artisan config:clear, then php artisan cache:clear, then php artisan route:clear, then php artisan view:clear. If the error persists, check storage/logs/laravel.log for the actual error message. The issue might be unrelated to the APP_KEY, such as a database connection failure, a missing PHP extension, or a syntax error in your routes or service providers.
How do I fix Laravel 500 errors on shared hosting where I cannot use the terminal?
On shared hosting without SSH access, you can still fix most issues through your file manager. Check that the .env file exists in the project root with correct database credentials. Set directory permissions for storage/ and bootstrap/cache/ to 775 using your hosting panel file manager. If you need to clear the config cache, delete all files inside bootstrap/cache/ except the .gitignore file. For composer autoload issues, you may need to run composer dump-autoload locally, then upload the updated vendor/composer/ directory via FTP.
Scan Your Laravel App for Exposures
Exposed debug pages, open .env files, and misconfigured servers are the top security risks for Laravel deployments. Check yours in 30 seconds.
Run Free Security ScanThe Bottom Line
The Laravel 500 Internal Server Error is almost never a mystery. The fix is nearly always hiding in storage/logs/laravel.log. When it is not, it is a permissions issue preventing the log file from being written in the first place. Follow the steps in this guide from top to bottom: read the log, check the .env, fix permissions, verify the key, rebuild autoload, clear caches, test the database connection, and confirm PHP compatibility. You will find the problem every single time.
Once your app is running, take two minutes to verify your deployment is not accidentally leaking sensitive files. A misconfigured .env file or an exposed debug page can undo all your hard work in an instant.
Related reading: The Danger of Exposed .env Files, CORS Preflight Request Blocked Fix. Related tools: Exposure Checker, ENV Validator, SSL Checker, Chmod Calculator, and 70+ more free tools.
Usman has 10+ years of experience securing enterprise infrastructure, managing high-traffic servers, and building zero-knowledge security tools. Read more about the author.