500 Internal Server Error
A 500 Internal Server Error is the HTTP status code a web server returns when it hits an unexpected condition and cannot finish the request. It is a server-side failure, not a client one: the browser sent a valid request to an address that exists, but something inside the application, its configuration or its environment broke while a response was being prepared. Because the server deliberately hides the details, the visitor sees only a vague apology, while the real reason is written to the server's error log.
What it means
The 5xx family of status codes reports a fault on the server side of the exchange. The client did its part — it opened a connection to a reachable host and sent a syntactically valid request — but the server could not produce a usable response. A 500 is the least specific member of that family. It does not distinguish between a bug in application code, a misconfigured web server, a database that refused a connection, or a disk that filled up mid-request. It simply states that the server encountered a condition it was not prepared to handle.
For a developer the code is both a signal and a starting point. The useful evidence is never on the page the visitor sees; it is in the server's log, where the actual exception, stack trace or configuration failure is recorded. Reading the 500 alone tells you that a request failed. Reading the log tells you why, and in almost every case the log entry is what actually points at the fix.
It is also worth keeping the code in context. A 500 that happens for every visitor usually points at configuration or a crashed process, while a 500 that appears only for certain requests points at a specific input or code path. That distinction shapes where you look first.
Common causes
- An unhandled exception in the application — a missing value, a failed database query, or an input the code never validated.
- A misconfigured web or application server, such as a broken rewrite rule, a directive the version does not support, or a damaged local config file.
- A dependency the site relies on is unavailable: a database, cache, or upstream service that refused the connection or returned an error.
- The application process crashed or was killed, often by an out-of-memory condition or a faulting extension.
- Resource limits — the process cannot write to a directory it needs, or the disk holding logs or uploads is full.
How to fix it
- Read the log before touching anything. The web server and the application usually record the real error at the moment it happens.
tail -n 50 /var/log/nginx/error.log journalctl -u myapp --since "10 min ago" - Check whether the application process is still running. A crashed or repeatedly restarting service produces 500s for every request.
systemctl status myapp systemctl restart myapp - Verify dependencies. Test the database and cache directly rather than assuming they are healthy.
mysqladmin ping redis-cli ping - Confirm permissions and disk space. Writable directories and free space are frequent culprits on new or migrated servers.
df -h ls -ld /var/www/app/storage - Reproduce with debugging on. Enable verbose logging temporarily, replay the failing request, then read the trace and turn the verbosity back down so internals are not exposed to visitors.
How to prevent it
Structured logging and alerting turn a 500 from a surprise into a notification. Route application exceptions to a file or log service, watch the rate of 5xx responses, and alert when it rises. A health endpoint that tests the database and cache, polled by your monitoring, will usually warn you before visitors notice.
Test changes somewhere other than production. Most 500s after a deploy come from configuration that differs between environments or from a code path that only the live data exercises. Keep a rollback ready, set sensible memory and upload limits so a single request cannot exhaust the process, and show visitors a friendly error page that hides internals while the log keeps the detail you need.