A 500 Internal Server Error is the server that runs the site admitting that something went wrong inside its own code, and that it cannot show you the page because of it. Not “the page does not exist”, not “you are not allowed”, not “come back later” - just an honest, unhelpful “something broke in here, and it was not your fault.”

The word to notice is internal. This is the server talking about itself, not about a network, a permission, or a thing you did. It ran your request, hit an error it did not know how to handle, and the only truthful thing left to say was 500. Which is why the message is so vague: the server is not hiding the detail to be difficult, it is refusing to leak its own internals to a stranger.

Why it tells you nothing

A 500 is the most generic error on the web on purpose. When an application fails in a way it did not anticipate - an exception nobody caught, a query that blew up, a null where an object was expected - the safe thing to show the outside world is a blank wall. The specifics of what broke, the stack trace, the line number, the query that failed, all of it stays on the server, in a log, where only the people running the site can read it.

So the 500 you see and the reason for it live in two different places. The browser has the code; the server has the cause. That gap is the whole difficulty of a 500, and it is why “I got a 500” is a report that tells a developer almost nothing on its own.

500 against 502 and 504, which look the same from outside

All three are the server’s family of “it went wrong on our end”, and telling them apart points at very different causes.

500 Internal Server Error
The application ran and its own code failed - an unhandled exception, a crash inside the request. The server answered; the answer was an error it made itself
502 Bad Gateway
A front server got an unusable answer from the application behind it - crashed, refused, malformed. The failure is between two servers
504 Gateway Timeout
The application did not answer in time at all. The front server gave up waiting

Roughly: a 500 is the application failing while it runs, a 502 is a proxy failing to get a good answer from it, and a 504 is the application being too slow to answer. If you got a 500, the code ran and broke; if you got a 502 or 504, it often never finished running at all. That distinction is the first thing worth knowing, because it decides whether you look in the application logs or at the plumbing in front of them.

If you are the visitor

Reload once, because a fair number of 500s are a single bad moment - one request that hit a race, one query that timed out under a burst - and the next attempt lands somewhere healthy. If it clears, there is nothing to chase.

If it persists, the fault is on the site’s side, and there is genuinely nothing in your browser that reaches a bug in their code. Clearing your cache, trying incognito, switching browsers - none of it touches the server, and each is a common half-hour spent treating a 500 as if it were a problem you could fix from your seat. The one useful thing you can do is tell them, with enough detail that they can find the matching line in their log: what you were doing, and roughly when.

If it is your site

A 500 means your code threw something it did not handle, so the question is only what, and the answer is in your logs rather than in the response the user saw. A few causes account for most of them.

An unhandled exception in the request. The most common by far. A method called on a nil, a key that was not there, a type that was not what the code assumed. The request reached your code, your code raised, and nothing caught it. Your application log has the stack trace; the user’s 500 does not.

A failed database call. A query against a column that was renamed, a connection pool exhausted under load, a migration that ran on one server and not another. These often arrive in bursts, because they track load or a deploy rather than a single input.

A configuration or environment problem. A missing environment variable, a secret that was not set, a service the app expects to reach and cannot. These are the ones that turn every request into a 500 at once, usually right after a deploy or an infrastructure change, and they look alarming precisely because nothing in the code changed - the ground under it did.

A bad deploy. New code that raises on a path the tests did not cover, or boots against a schema that is not there yet. If the 500s started at a release, that is the first place to look, and a rollback is faster than a diagnosis.

The reason a 500 is so hard to chase after the fact

The error the user saw carries none of the cause, so a 500 reported an hour later, from memory, is close to useless: you have a generic code and a rough time, and you are grepping a log for a needle you can only describe as “around three o’clock.” The stack trace that would have named the bug is in a log line nobody captured against the request that produced it.

What closes a 500 quickly is the request that caused it, its exact time, and what the person was doing - captured while it happened, so you can match it to the one log line that has the real error in it. That is the difference between a bug you find in a minute and a log you scroll for an afternoon.

Session Replay

Free Chrome extension. One click on the page that is misbehaving captures the screenshot, the console and the network log, and hands you a link to paste into the ticket.

Get the extension

The network log holds the failing request with its 500 and the exact moment it happened, so whoever picks the report up can line it up against the server log and read the real error - rather than reproduce the whole thing first to find out what a generic code was hiding.

In one paragraph

A 500 Internal Server Error means the application ran your request and its own code failed in a way it did not handle, so it showed the one safe, generic answer it had - which is what makes it different from a 502, where a proxy could not get a good answer from the app, and a 504, where the app was too slow to answer at all. If you are visiting, reload once and then assume it is their side. If it is yours, the cause is in your logs, not the response: suspect an unhandled exception, a failed query, a missing config, or the last deploy - and capture the failing request while you have it, because a 500 tells you nothing on its own.