DEVESSENTIALS

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.

5xx · Server errorRFC 9110 §15.6.1

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 digest you can search for in the server logs.
  • PHP fatal errors with display_errors off, 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.app

Express: 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

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

Browse all developer guides →

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.