Skip to content

HTTP status code · Server errors (5xx)

500 Internal Server Error

500 Internal Server Error means the server ran into an unexpected condition and could not complete the request. It is the generic catch-all for server failures, and the most common cause is an unhandled exception in the application code: a null value, a failed database query, a missing environment variable.

Facts about this status code
Class5xx, Server errors
Defined inRFC 9110 §15.6.1
Cacheable by defaultOnly with explicit Cache-Control or Expires
Safe to retryOnce, maybe; a 500 caused by a bug fails the same way every time
Relevant headers
  • Content-Type: application/problem+json (RFC 9457) gives API clients a machine-readable error without leaking a stack trace

What 500 means

RFC 9110, section 15.6.1, defines 500 in a single sentence: the server encountered an unexpected condition that prevented it from fulfilling the request. There is no more specific meaning than that. It is the default most frameworks fall back to when your code throws and nothing catches it: Express, Next.js route handlers, FastAPI and Django all turn an uncaught exception into a 500.

Because it is generic, the status code alone tells you almost nothing about the cause. The useful information lives in the server logs, at the exact timestamp of the failing request. The response body should stay vague on purpose: a stack trace or SQL error sent to the browser helps attackers more than users.

A 500 comes from the application itself, not from a proxy in front of it. If nginx, a load balancer or Cloudflare cannot reach your app at all, you get a 502, 503, 504 or a 52x instead. Seeing a 500 is actually a sign that the request did reach your code.

Common causes

If you are visiting the site

  • A bug on the site triggered by the specific page, form input or account you are using; other pages may work fine.
  • A broken deploy or a database outage on the site side, which usually affects everyone at once.
  • Occasionally a stale or corrupted session cookie that the server fails to parse.

If you run the server

  • An unhandled exception: reading a property of undefined, a failed JSON.parse, a type error on input you did not expect.
  • A dependency failing inside the request: the database refuses connections, a query times out, a third-party API returns something the code does not handle.
  • Configuration missing in production only: an unset environment variable, a wrong secret, a file path that exists on your laptop but not in the container.
  • Wrong permissions on files the app writes to (uploads, cache, sessions), or a .htaccess directive Apache does not understand, which Apache reports as a 500.
  • A PHP fatal error or memory limit, which PHP-FPM turns into a 500 with an empty page when display_errors is off.

How to fix it

If you are visiting the site

  • Reload once. If the 500 came from a brief hiccup, such as a deploy restarting the app, the second try often works.
  • Try another page on the same site. If only one page or one action fails, the bug is specific to it and only the site owner can fix it.
  • If it only happens while logged in, log out or clear the cookies for that site and try again.
  • Report it to the site with the time and the URL; that is exactly what the developer needs to find the log entry.

If you run the server

  • Read the application log at the timestamp of the failing request, not the proxy log: the stack trace is there. On a VPS try journalctl -u your-app --since "10 min ago"; on PaaS hosts, the platform log viewer.
  • Reproduce with the same input locally, add a test for it, then fix the code path. Validate input at the edge so bad data becomes a 400 or 422 instead of a 500.
  • Compare production configuration with what the code expects: missing environment variables are the most common reason something works locally and fails after deploy.
  • Register one error handler that logs the full error with a request ID and returns a generic body, so every 500 is traceable and nothing internal leaks.
  • Add error tracking (Sentry or similar) and alert on the 5xx rate, so you learn about 500s before users report them.

How to send 500

You rarely write status(500) by hand. The job is to catch unexpected errors in one place, log them with enough context, and answer with a generic 500 body.

Express (Node.js)
app.get('/orders/:id', async (req, res) => {
  // Express 5 forwards a rejected promise here to the error handler
  const order = await db.orders.findById(req.params.id);
  res.json(order);
});

// Error handler: four arguments, registered after all routes
app.use((err, req, res, next) => {
  console.error(err);
  res.status(500).json({ error: 'Internal server error' });
});
Next.js App Router route handler
// app/orders/[id]/route.ts
export async function GET(
  _request: Request,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params;
  try {
    return Response.json(await getOrder(id));
  } catch (err) {
    console.error(err);
    return Response.json({ error: 'Internal server error' }, { status: 500 });
  }
}
Go net/http
mux.HandleFunc("GET /orders/{id}", func(w http.ResponseWriter, r *http.Request) {
	order, err := store.Order(r.Context(), r.PathValue("id"))
	if err != nil {
		log.Printf("get order %s: %v", r.PathValue("id"), err)
		http.Error(w, "internal server error", http.StatusInternalServerError) // 500
		return
	}
	json.NewEncoder(w).Encode(order)
})
Python FastAPI
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

app = FastAPI()

# Catch-all for exceptions nothing else handled
@app.exception_handler(Exception)
async def unhandled_error(request: Request, exc: Exception):
    return JSONResponse(status_code=500, content={"error": "Internal server error"})

Commonly confused with

500 vs 502
500 is raised by the application itself; 502 is raised by a proxy or gateway that got an invalid response, or none, from the application behind it.
500 vs 503
503 is a deliberate "not right now" for overload or maintenance, often with Retry-After; 500 is an accident the server did not plan for.
500 vs 400
If bad input crashes your handler, the honest answer is 400 or 422 after validation, not a 500: the client is at fault, not the server.

Frequently asked questions

Is a 500 error my fault or the website's?
Almost always the website's. A 500 means the server failed while handling a request it accepted. The only visitor-side factor that sometimes helps is clearing that site's cookies, in case a broken session is what the server chokes on.
How do I find the cause of a 500 error?
Look in the application logs at the exact time of the failing request. The HTTP response is intentionally vague; the stack trace, the failing query or the missing variable shows up in the server-side log, the error tracker or the platform log viewer.
Should my API return 500 for validation errors?
No. Use 400 for malformed requests and 422 for well-formed requests with invalid values. Reserve 500 for failures the client could not have prevented, so client developers know whether changing the request can help.
Does a 500 error hurt SEO?
A short burst does not. Googlebot slows its crawl when it sees many 5xx responses, and pages that keep returning 500 for days can drop out of the index. Fix the cause quickly and the pages are recrawled normally.
Why does my site show a blank page instead of an error message?
PHP with display_errors off, and many production builds, send a 500 with an empty body on purpose. Check the status in the browser Network tab, then read the PHP-FPM or application log for the actual error.

Last reviewed by Arielton Oberek.