Rejoice Software logo

Laravel · · 4 min read

Fixing 500 errors in Laravel — a battle-tested checklist

From storage permissions to queue workers, this is the exact order we run through when a Laravel app is down.

Start with the log

A “500 Server Error” page tells you only that something failed. The reason is in the logs. Look at storage/logs/laravel.log first (or the daily file if you use daily logs). If it is empty, the error happened before Laravel could log it, so check the web server error log and the PHP-FPM or LiteSpeed error log next.

Do not set APP_DEBUG=true on a live site to see the error. It shows stack traces, environment values and sometimes passwords to anyone who opens the page. Read the logs instead.

tail -n 100 storage/logs/laravel.log
# no entries? check the server logs, for example:
tail -n 100 /var/log/nginx/error.log

1. Permissions

Laravel must be able to write to storage and bootstrap/cache. After an upload by FTP or a deploy as a different user, these folders are often owned by the wrong account. The log will show “Permission denied” or “failed to open stream”. Give the web server user write access to those two folders only — never make the whole project writable.

2. Environment and configuration cache

A missing or wrong .env value is a classic cause: no APP_KEY (“No application encryption key has been specified”), wrong database password, or a cache driver that does not exist on the server. Remember that after php artisan config:cache, Laravel no longer reads .env at runtime, and any env() call outside config files returns null. Change .env, then rebuild the cache.

3. Dependencies and autoloading

“Class not found” right after a deploy usually means vendor was not updated or the autoloader is stale. Run composer install with --no-dev and --optimize-autoloader on the server, and make sure the server’s PHP version and extensions match what composer.lock requires.

4. Database and migrations

New code that expects a column the database does not have yet produces SQL errors such as “Unknown column” or “Base table or view not found”. Run pending migrations with --force in production. Also check that the database and Redis are reachable and not out of connections.

5. Stale caches and queue workers

Cached routes, views and config from the previous release can point at code that no longer exists. Clear and rebuild them as part of every deploy. Queue workers are long-running processes: they keep the old code in memory until they restart, so jobs can fail with errors that no longer match your code.

composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan optimize:clear
php artisan config:cache && php artisan route:cache && php artisan view:cache
php artisan queue:restart

6. Memory, timeouts and outside services

Large exports, image processing or unbounded queries can exceed PHP’s memory_limit or max_execution_time. Move heavy work to queued jobs and process data in chunks. Calls to outside APIs should always have a timeout and a fallback, so one slow provider cannot take your pages down. Note the difference between codes: a 502 or 504 usually points to PHP-FPM or a proxy timeout, not an application exception.

Prevent the next one

  • Use one deploy script that always runs the same steps in the same order.
  • Add error tracking (for example Sentry or Flare) so you hear about errors before customers do.
  • Monitor the /up health route that Laravel includes, plus a page that touches the database.
  • Keep staging as close to production as possible: same PHP version, extensions and cache drivers.

Want us to look at your site?

Run the free website checker for an instant report, or create a ticket and an engineer will reply within one working day.

#Laravel #Debugging #DevOps

Let's talk

Let's build something worth talking about.

Free 30-minute strategy call. No pitch, no obligation — just a plan.

Start a project