HTTP 500, 502, 503 and 504 errors: what they mean

A 5xx status means the server failed, not the visitor. 500 is a crash in your code, 502 is an invalid reply from the app behind a proxy, 503 is temporary unavailability, and 504 is a proxy timeout.

Updated 8 min read

Learning objectives

After reading this article you will be able to:

  • Tell 500, 502, 503 and 504 apart from the exact symptom
  • Find the cause in your app logs and proxy logs
  • Fix the most common causes and keep them from coming back
On this page

Every status code from 500 to 599 says the same thing: the request was valid, but the server could not complete it. The visitor did nothing wrong, and the fix is on your side. The four codes you will meet most often look similar in the browser but point to very different places in the request chain.

The trick is to find out who produced the error. A bare "500 Internal Server Error" page from your framework means your code failed. A "502 Bad Gateway" page, plain or branded by nginx, a CDN or a load balancer, means the proxy in front of your app is complaining about the app.

who raises the codeBrowserProxyApp500Internal Server Errorexception in the code502Bad Gatewayinvalid response503Service Unavailableoverload or maintenanceor504Gateway Timeoutno timely response
A 500 comes from your app; 502 and 504 come from the proxy in front of it; a 503 can come from either.

What does each 5xx code mean?#

Code Name Raised by Plain meaning
500 Internal Server Error The app An unexpected condition stopped the app from answering
502 Bad Gateway A proxy or gateway It received an invalid response from the upstream server
503 Service Unavailable App or proxy Temporarily unable to handle the request (overload or maintenance)
504 Gateway Timeout A proxy or gateway It did not get a timely response from the upstream server

These definitions come from RFC 9110, section 15.6. The distinction that matters: 502 and 504 are only produced by something sitting between the visitor and your app. With nothing in front of your app, you will rarely see them, unless your own code acts as a gateway to another service.

Why do I get a 500 Internal Server Error?#

A 500 is the catch-all. Your code threw an exception that nothing handled, and the framework answered with 500. The browser tells you nothing useful; the log tells you everything.

Common causes, from most to least frequent:

  1. An unhandled exception: reading a property of undefined, a failed JSON parse, a missing field.
  2. A failed dependency: the database is unreachable, credentials are wrong, a third-party API timed out.
  3. A missing environment variable or config file that works on your machine but not in production.
  4. Running out of memory, which kills the process mid-request.
  5. A bad deploy: a missing module or a build that did not finish.

To diagnose, reproduce the request and read the stack trace in the app logs. Then make sure errors are caught and logged instead of leaking out to the visitor:

js
import express from "express";

const app = express();

app.get("/users/:id", async (req, res) => {
  const user = await findUser(req.params.id); // may throw
  if (!user) return res.status(404).json({ error: "not found" });
  res.json(user);
});

// Express 5 forwards rejected promises from async handlers here
app.use((err, req, res, next) => {
  console.error(`${req.method} ${req.url}`, err);
  res.status(500).json({ error: "internal error" });
});

app.listen(process.env.PORT ?? 3000, "0.0.0.0");

Note the 404 for a missing user. Returning 500 for a normal "not found" case floods your logs and hides real failures.

Why do I get a 502 Bad Gateway?#

A 502 means the proxy reached out to your app and got garbage, or nothing. As MDN puts it, a server acting as a gateway received an invalid response from the upstream server. In practice the app is almost always down or unreachable. Check these in order:

  1. The app crashed or is not running. The proxy has nothing to talk to. Look at the process: did it exit, loop on restarts, or get killed for memory?
  2. The app is still starting. Right after a deploy or restart, the port is not open yet. Slow boots (migrations, large model loads) make this last longer.
  3. Wrong port or address. The proxy forwards to port 3000 but the app listens on 8080. Or the app binds to 127.0.0.1 inside a container, so nothing outside can reach it. Bind to 0.0.0.0 and read the port from the environment.
  4. The app closed the connection mid-response, for example a crash during a request or a keep-alive timeout shorter than the proxy's.
  5. A proxy rule points at the wrong upstream, such as a typo in a proxy_pass or a stale backend address.

If your app works when you open its port directly but fails behind the proxy, the problem is in the proxy config, not in the code. See what a reverse proxy does for how that hop works.

Why do I get a 503 Service Unavailable?#

A 503 says "not right now". Per RFC 9110, the server is temporarily unable to handle the request because of overload or scheduled maintenance, and it may send a Retry-After header to say when to try again (section 10.2.3).

Typical causes:

  • Every worker or connection slot is busy, so new requests are rejected or queued out.
  • No healthy backend is left: a load balancer marked all of them as failing health checks.
  • A deliberate maintenance mode.
  • A rate limiter or upstream service shedding load.

If you return 503 on purpose, say when to retry, so crawlers and clients back off politely:

js
app.use((req, res, next) => {
  if (process.env.MAINTENANCE === "1") {
    res.set("Retry-After", "600"); // seconds
    return res.status(503).send("Back in about 10 minutes");
  }
  next();
});

To fix real overload, find the bottleneck first: CPU pegged, memory full, a slow query holding every connection. Adding workers does not help if the database is the limit.

Why do I get a 504 Gateway Timeout?#

A 504 means the proxy connected to your app, but the app took too long to answer. The app is alive, just slow. Nginx, for example, waits 60 seconds by default between two successive reads from the upstream (proxy_read_timeout 60s), per the nginx proxy module docs. Other proxies and CDNs have their own limits, often shorter or fixed.

Typical causes:

  • A slow database query, a missing index, or a lock.
  • A request that calls another API which hangs, with no timeout of your own.
  • Heavy work done inside the request: image processing, report generation, big exports.
  • A saturated app where requests sit in a queue before being served.

The fix is almost never "raise the proxy timeout". Move long work out of the request, answer quickly with a job id, and let the client poll or receive a webhook. Add explicit timeouts to every outbound call so a hung dependency fails fast with a clear error. If you do need a longer limit for one route, raise it for that route only. To understand the layer that spreads requests across instances and can also cut them short, see what a load balancer is.

How do I diagnose a 5xx error step by step?#

  1. Identify who answered. Check the response headers and the page body. A Server header or a CDN-branded error page tells you which layer failed.
  2. Read the app logs at the time of the error. A stack trace means 500. No trace and no request at all means the proxy never reached the app. The guide on how to read your app's logs helps with this step.
  3. Check the process. Is it running, how long has it been up, did it restart recently, how much memory does it use?
  4. Call the app directly, bypassing the proxy, with curl -i http://localhost:3000/health. If this works, look at the proxy config.
  5. Check dependencies. Database, cache and external APIs. A slow or dead dependency produces 500, 503 and 504 alike.
  6. Look at the pattern. One route points to a code bug. Every route points to a dead app or proxy. Intermittent errors under load point to capacity or timeouts.

A /health endpoint that checks the database and returns 200 or 503 saves time here, and load balancers can use it to pull a failing instance out of rotation. For a wider checklist when the whole site is unreachable, see why is my website down.

How do I prevent 5xx errors?#

  • Log every unhandled error with the request path and a request id.
  • Set timeouts on every database and HTTP call your app makes.
  • Restart the process automatically if it exits, and alert on repeated crashes.
  • Load test before launch to find where capacity runs out.
  • Run migrations and slow startup work before the app accepts traffic, not during.
  • Track the share of 5xx responses over time. A rising line warns you before users complain.

How to do this on Square Cloud#

On Square Cloud, each web application runs in its own isolated container, and a 5xx investigation starts the same way: with the logs. They stream in real time in the dashboard, the CLI, the VS Code extension and the mobile app, which makes a crash or stack trace easy to catch right after a request fails.

A 502 after a deploy usually means the app is not listening on 0.0.0.0 on the port in the PORT variable, so check that first. Containers are always on, with no cold start or sleep mode, and automatic restart (on by default for dashboard deploys) brings back an application that exits with an error. Running out of memory is different: the application stops and stays stopped until you raise its RAM in the settings and start it again. Web applications need at least 512 MB of RAM.

To see how often errors happen, the web analytics included in every plan show routes and status codes, collected on the server. Pro and Enterprise plans add p50, p95 and p99 latency and HTTP logs, useful for spotting the slow routes behind 504s. Your site also gets a *.squareweb.app subdomain with HTTPS, so you can test the app directly before attaching a custom domain.

The site hosting features are on the Sites page.

Website hosting

Static and SSR sites with automatic TLS and a global CDN

Learn more

FAQs

What is the difference between a 502 and a 504 error?

A 502 means the proxy got an invalid response (or none at all, because the connection was refused or dropped). A 504 means the proxy connected but the app did not answer in time.

Is a 5xx error my fault or the visitor's fault?

The 5xx class means the server failed to handle a valid request, so the cause is on the server side. Reloading, clearing cookies or switching browsers rarely helps.

Does a 503 error hurt SEO?

A short outage usually does little harm: Google slows crawling when it sees 5xx errors and keeps already indexed URLs for a while. URLs that keep returning a server error are eventually dropped from the index.

Why do I get a 502 right after deploying?

Usually the app crashed on startup, is still booting, or listens on a different port or address than the proxy expects. The app logs show which one.

Next articleSSR vs SSG vs static: what changes when you hostSSR builds HTML on every request, SSG builds it once at build time, and a static site is just ready-made files. See what each needs in servers, caching and cost.

More in Websites

See all 20 articles