HTTP 500 Internal Server Error
500 is the catch-all server error: something went wrong and no more specific 5xx applies. Usually an exception in application code was caught at the top level by the framework, which returned 500.
500 Internal Server Error
The server encountered an unexpected condition that prevented it from fulfilling the request.
Who fixes it: Server. The client can only retry later. The fix is in the application, its configuration or a dependency, and the details are in the server logs.
What 500 means
Because it's generic, the response itself rarely says much, and in production it shouldn't: stack traces leak internals. The answer is in the server logs.
Retrying can help with transient failures such as a dropped database connection, but not with bugs. Clients should retry only idempotent requests, with backoff.
Source: RFC 9110 §15.6.1.
Common causes
- An unhandled exception:
TypeError: Cannot read properties of undefined, a null from the database, an unexpected third-party response shape. - A missing or wrong environment variable in production (
DATABASE_URL, API keys) that was set locally. - Database problems: exhausted connection pool, a migration that hasn't run, a query referencing a column that doesn't exist yet.
- Express 4 doesn't catch errors from async handlers. Without
next(err)the rejection is unhandled and the request hangs or the process crashes. Express 5 forwards rejected promises to the error handler. - Next.js: an error during server rendering or in a Route Handler. In production the browser only sees a generic message with a
digestyou can search for in the server logs. - PHP fatal errors with
display_errorsoff, or an invalid directive in Apache's.htaccess. - Serverless: the function crashed or ran out of memory. Vercel reports
500: FUNCTION_INVOCATION_FAILED.
How to find and fix a 500
Find the error in the logs
The stack trace is almost always there.
# nginx in front of your app
sudo tail -f /var/log/nginx/error.log
# App running under systemd
journalctl -u myapp -f
# Docker
docker logs -f --tail 200 myapp
# Vercel
vercel logs https://my-app.vercel.appExpress: catch async errors and return a safe response
Log the details with an ID and return only the ID to the client. The error handler must have four parameters.
// Express 4: forward async errors (Express 5 does this for you)
const asyncHandler = (fn) => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);
app.get('/api/report', asyncHandler(async (req, res) => {
res.json(await buildReport(req.query));
}));
// Last middleware
app.use((err, req, res, next) => {
const id = crypto.randomUUID();
console.error(id, err);
res.status(500).json({ error: 'internal_error', id });
});Next.js: an error boundary that shows the digest
error.tsx catches rendering errors for its segment. The digest matches the entry in the server logs.
// app/dashboard/error.tsx
'use client';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<div>
<p>Something went wrong. Reference: {error.digest}</p>
<button onClick={() => reset()}>Try again</button>
</div>
);
}Reproduce production locally
Many 500s only happen in production builds. Run npm run build && npm start with production-like environment variables and replay the failing request with curl -i.
Headers that matter with 500
- Cache-Control: no-store
- Stops a CDN from caching a transient error page.
- X-Request-Id
- Not standardised, but a request ID in the response makes it easy to find the matching log line.
500 vs similar status codes
- 502 Bad Gateway: A proxy couldn't get a valid response from the app at all. With 500, the app itself responded with the error.
- 503 Service Unavailable: A known, temporary condition (maintenance, overload). Tells clients that retrying later is expected to work.
- 504 Gateway Timeout: A proxy gave up waiting for the app.
SEO impact of 500
When Googlebot sees many 5xx responses it slows its crawling. A short outage is fine, but URLs that keep returning 500 are eventually dropped from the index. Watch your 5xx rate and the server errors in Search Console's Crawl Stats report.
Related status codes
502Bad Gateway
A gateway or proxy received an invalid response from the upstream server it contacted.
503Service Unavailable
The server is temporarily unable to handle the request, due to overload or scheduled maintenance.
504Gateway Timeout
A gateway or proxy didn't receive a timely response from the upstream server.
Looking for another code? Search the full HTTP status codes list, or read which code your API should return in the REST API status codes guide.
Learn more
HTTP Status Codes for REST APIs: 200 vs 201 vs 204, 400 vs 422 vs 429
A practical guide to choosing the right HTTP status code in REST API design. Covers the most confusing pairs: 200 vs 201, 400 vs 422, 401 vs 403, and when to use 503 vs 429.
REST API Design Best Practices: A Practical Guide
The conventions that make REST APIs intuitive and consistent. Covers URL design, HTTP methods, status codes, versioning, pagination, and error responses.
CORS Explained: Why It Happens and How to Fix It
CORS errors happen when a browser blocks a request to a different origin. Learn how the Same-Origin Policy works, what preflight requests are, and how to configure CORS correctly in Express, FastAPI, and Nginx.
Frequently Asked Questions
Is a 500 error my fault or the website's?▾
The website's. A 500 means the server failed while handling a request it had accepted. As a visitor you can only retry later; as the developer, check the server logs for the exception and stack trace.
What's the difference between 500 and 502?▾
500 comes from the application itself: it ran and failed. 502 comes from a proxy or gateway such as nginx, a load balancer or a CDN that couldn't get a valid response from the application at all, because it was down, crashed mid-response or closed the connection.
Why does my app return 500 only in production?▾
The usual suspects are missing environment variables, a database without the latest migrations, a different Node or runtime version, and code paths that only run in production builds. Reproduce with a local production build using production-like configuration.