HTTP 500 Internal Server Error

Internal Server Error

HTTP 500 is Internal Server Error: the server hit an unexpected condition that kept it from fulfilling the request (RFC 9110 section 15.6.1). It’s the generic server-side failure, the one a framework sends when your code throws and nothing catches it. The detail belongs in your logs; the client gets the bad news and, ideally, an id it can quote.

The exchange

HTTP/1.1 500 Internal Server Error
Content-Type: application/problem+json

{"title": "Internal Server Error", "status": 500, "instance": "/errors/7f3c2a91"}

Keep stack traces, SQL and file paths out of the body. Django with DEBUG = True and Express when NODE_ENV isn’t production put the stack trace in the page, which is one more reason those settings must be off in production.

What the client should do

Treat it as possibly transient. Retry idempotent requests (GET, PUT, DELETE) a few times with backoff. Don’t blindly repeat a POST: the server may have done part of the work before it failed. A 500 that comes back every time is a bug, and retries won’t fix it.

A better code, when there is one

the client sent something you can't handle       a 4xx: 400, 404, 409, 422
overloaded, or down for maintenance              503 with Retry-After
a dependency is down and there's no fallback     503
as a proxy: the upstream answered badly          502 Bad Gateway
as a proxy: the upstream timed out               504 Gateway Timeout
the method isn't implemented                     501 Not Implemented

A 500 for a missing record or a typo in the input sends clients into retries and pages your on-call engineer for the user’s mistake. Search 500 502 503 504 in the tool below to see the four side by side.

Gotchas by stack

  • Go doesn’t send a 500 when a handler panics. net/http recovers the panic, logs it and drops the connection, so the client sees a reset, and nginx or a load balancer in front reports 502. Recovery middleware that writes the 500 fixes it.
  • Express 4 misses rejected promises. An async handler that rejects never reaches the error handler, and on Node 15 and later an unhandled rejection ends the process. Express 5 forwards the rejection to the error handler, which sends 500.
  • nginx’s own 500s usually come from its configuration; rewrite or internal redirection cycle in the error log is the classic.
  • Django renders your 500.html template when DEBUG is off; in Flask, register @app.errorhandler(500).

500 isn’t heuristically cacheable, though CloudFront caches a 500 from the origin for 10 seconds by default. Googlebot slows its crawling when it sees 5xx errors; pages already indexed survive a short burst but are dropped if the errors persist.

In your stack

app.use((err, req, res, next) => {        // Express: four arguments, registered after the routes
  console.error(err);
  res.status(500).json({ title: 'Internal Server Error', status: 500 });
});
func recoverer(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if v := recover(); v != nil {
                log.Printf("panic: %v", v)
                http.Error(w, "internal server error", http.StatusInternalServerError)
            }
        }()
        next.ServeHTTP(w, r)
    })
}

Try it in the HTTP Status Codes

500 Internal Server Error

  1. 500Internal Server Error5xx Server error

    Something went wrong on the server and it has no better way to say what.

    When to use it

    The last resort for an unhandled fault, where the logs carry the detail and the client gets nothing but the bad news.

    Common mistakes

    Using it for input the client can fix, and for expected failures like a missing record that has its own code.

    Not cacheable by defaultRFC 9110

Every code, by class

Codes marked unofficial come from one server’s own convention — nginx, Cloudflare, cPanel, Microsoft — and are not in the IANA registry.

Runs in your browser. Nothing you enter is sent anywhere.