500 Internal Server Error
Server error response defined by RFC 9110 §15.6.1.
Last updated July 27, 2026
Common causes at a glance
- An unhandled exception escaping a request handler
- Database connection pool exhausted or the database refusing connections
- A missing or renamed environment variable after a deploy
- A worker killed by the OOM killer mid-request
Reproduce it
curl -sS -o /dev/null -w 'code=%{http_code}\n' https://api.example.com/v1/ordersWhat 500 Internal Server Error means
500 is what a server says when it has nothing useful to say. Every other 5xx code names a specific failure mode; 500 is the residue. A handler threw, a driver raised on a closed connection, a template referenced a field that was not there. The framework catches whatever escaped and emits 500 because the alternative is dropping the connection.
The operationally important property is that 500 tells the client nothing on purpose. The body must never contain a stack trace, a SQL statement, a filesystem path, or a framework debug page. Those leak directory layout, dependency versions, and sometimes credentials to anyone who can trigger the error. From the outside a leaky 500 and a clean one look identical to monitoring, which is how a staging debug renderer survives in production for months.
What the spec says
RFC 9110 §15.6.1 defines 500 in two lines: the server encountered an unexpected condition that prevented it from fulfilling the request. The surrounding §15.6 sets the class-wide rule that a 5xx server is declaring itself aware that it erred. The spec says nothing about the body, so the shape of your error payload is yours; RFC 9457 problem details is the usual answer for APIs. Unlike 501, a 500 is not heuristically cacheable.
What actually causes it
Most real 500s are application bugs reached by a request shape nobody
tested: a query parameter that is absent rather than empty, a user record with a null
field, a payload far larger than the fixture. The second cluster is environmental — a
database that hit max_connections, a secret that failed to mount, a
migration applied to one replica but not the others. If the rate steps up at a release
boundary and stays there, the deploy is the cause; if it tracks traffic, look at pool
sizes and memory limits before reading any code.
How to debug it
First establish who generated it. An application 500 carries your framework’s headers and error format; a 500 emitted by nginx after the upstream died looks generic and will not appear in your application logs at all:
curl -sS -D - -o /dev/null https://api.example.com/v1/orders | head -20
Then pull the failing request out of your logs by request ID and read the trace. Before anything else, confirm the response is not leaking internals: if the body contains a file path or a stack frame, turn the debug renderer off now. The header inspector shows whether a proxy rewrote the status on the way out.
Working examples
curl
# -D - prints response headers; the Server header tells you which hop answered.
curl -sS -D - -o /dev/null https://api.example.com/v1/orders
# Loop a route to see whether the failure is deterministic or intermittent.
for i in $(seq 1 20); do
curl -sS -o /dev/null -w '%{http_code} ' https://api.example.com/v1/orders
done; echo
Python (requests)
import requests
# requests does not raise on 500; you have to check for it.
resp = requests.get("https://api.example.com/v1/orders", timeout=10)
print(resp.status_code, resp.headers.get("Server"))
if resp.status_code == 500:
# Truncate: a leaky server may be handing you a full stack trace.
print("error body:", resp.text[:400])
resp.raise_for_status() # raises requests.exceptions.HTTPError
Node (fetch)
// fetch() resolves normally on 500 — response.ok is the only signal.
const res = await fetch("https://api.example.com/v1/orders");
if (res.status === 500) {
console.error("server error from", res.headers.get("server"));
console.error((await res.text()).slice(0, 400));
} else if (!res.ok) {
console.error("other failure:", res.status);
} else {
console.log(await res.json());
}Reference and tooling
The authoritative list of assigned status codes is theIANA HTTP Status Code Registry. For a searchable copy with the semantics summarised, use theHTTP status code reference on this site. To see exactly what a server is sending, theheader inspector shows the raw status line and response headers, and thecurl builder assembles the request flags for you.
Other 5xx codes
- 501 Not Implemented
- 502 Bad Gateway
- 503 Service Unavailable
- 504 Gateway Timeout
- 505 HTTP Version Not Supported
- 506 Variant Also Negotiates
- 507 Insufficient Storage
- 508 Loop Detected
- 510 Not Extended
- 511 Network Authentication Required
- 520 Web Server Returns an Unknown Error
- 521 Web Server Is Down
- 522 Connection Timed Out
- 523 Origin Is Unreachable
- 524 A Timeout Occurred
- 525 SSL Handshake Failed
- 526 Invalid SSL Certificate
- 530 Unable to Resolve Origin Hostname