ByteScope

500

How to fix HTTP 500 Internal Server Error

5xx server errorIETF standardRFC 9110

The application itself ran, hit something it did not expect, and gave up — so unlike a 502 or a 504 there is a stack trace somewhere, and the whole job is finding which log holds it.

Why you are seeing 500, and what to do about it

The same three digits are three different problems depending on which side of the request you are on. Read the block that describes you.

Reproduce 500 with curl

Run this against the URL that failed. It prints the status without the error page, so you can see what the server said rather than what the browser rendered.

curl
curl -sS -o /dev/null -D - -w '\n%{http_code} in %{time_total}s\n' -H 'content-type: application/json' --data '{"quantity":-1}' https://example.com/api/orders

`-sS` silences the progress meter while keeping error messages, `-o /dev/null` throws away the body, `-D -` dumps the response headers to stdout, `--data` sends the payload and makes the request a POST, and `-w` prints the status and the wall-clock duration. Read the header block first, because it answers the only question the status line cannot: which layer wrote this. A `server:` naming your application framework, or any `x-request-id` your stack sets, means the request reached your code and the exception is in your log under that id. A `server: nginx` or `server: cloudflare` with no application headers at all means you are looking at an error page from in front of the application, and the three digits are worth re-reading — an intermediary that could not get a usable answer reports 502, not 500. The elapsed time is the second tell: an application-level 500 usually returns as fast as a success, because the exception was thrown rather than waited for, so a 500 that takes thirty seconds is a timeout inside your code and points at the dependency that hung rather than at the line that threw. Swap the payload for one you know is valid to confirm the endpoint itself works, and keep the failing one — it is the reproduction, and it is the thing the back end cannot recover from a log.

What 500 actually means

RFC 9110 §15.6.1 defines 500 as the server having encountered an unexpected condition that prevented it from fulfilling the request, sitting inside a 5xx class §15.6 introduces as the server being incapable of performing the request. The definition is deliberately the least specific one in the specification, and that is the useful part: 500 is an admission rather than a diagnosis, so the code carries no information at all about what broke, only about who noticed. Three consequences follow. First, a 500 your application produced is a perfectly valid HTTP response, so a proxy in front of it relays the response untouched instead of substituting one of its own — which is exactly why RFC 9110 §15.6.3 reserves 502 for the case where the intermediary received an invalid response, and why a 500 is evidence that your code ran while a 502 often means it never did. Second, 500 is absent from the list of heuristically cacheable status codes in RFC 9110 §15.1 — which names 200, 203, 204, 206, 300, 301, 308, 404, 405, 410, 414 and 501, and nothing else — so no intermediary will store one on its own initiative and a 500 that persists is being regenerated on every request rather than served from a cache. Third, 500 says nothing about whether the request had an effect: the specification gives the client no way to know how far the server got before the unexpected condition, which is why a 500 on a payment or an order is not automatically safe to retry. Servers also emit 500 from their own machinery, not only from application code — Apache's CGI documentation states that an "Internal Server Error" whose error log reads "Premature end of script headers" means the script emitted something before its HTTP headers, or that suexec's permission checks refused it, and nginx answers 500 for its own internal faults such as a rewrite or internal redirection cycle. In both cases the three digits are the same and the log line is the entire content.

Codes 500 gets mistaken for

Confused withHow to tell them apart
502500 is the application admitting its own failure; 502 is an intermediary reporting that the application never handed it a usable answer. RFC 9110 §15.6.3 defines 502 as an invalid response received from an inbound server, and an application's own 500 is a valid response, so a proxy passes it straight through. Practically: a 500 means there is a stack trace to find, while a 502 often means the worker died before any handler could run and the application log is empty.
503503 is a decision and 500 is an accident. RFC 9110 §15.6.4 defines 503 as the server being temporarily unable to handle the request due to overload or scheduled maintenance, and lets it carry `Retry-After` to say for how long. Nothing in 500 suggests waiting, because nothing about it is expected to pass. If your service sheds load by throwing, it is reporting a planned refusal as a crash, and every client backoff heuristic keyed on 503 will miss it.
400RFC 9110 §15.5.1 defines 400 as the server being unable to process the request due to something perceived to be a client error. That is the honest code for input your endpoint rejects. A 500 raised while validating input is a bug in the error handling rather than in the server: the request was answerable, the answer was "no", and the 5xx tells the client to retry something that will never succeed.
Cloudflare 520Through Cloudflare the two are easy to tell apart, and the difference is worth knowing. Cloudflare documents 520 as the origin returning an empty, unknown or unexpected response — including headers above its 128 KB limit — so a 520 means your origin's answer was not usable HTTP. A 500 arriving through Cloudflare is the opposite: a well-formed response your application produced deliberately, which Cloudflare relayed untouched.
501RFC 9110 §15.6.2 defines 501 as the server not supporting the functionality required to fulfil the request, and names it the appropriate answer when the server does not recognise the method. Unlike 500 it is a statement about the server's capabilities rather than about one request going wrong, and it is one of the few error codes RFC 9110 §15.1 lists as heuristically cacheable — so a mistaken 501 can be stored by a cache and repeated after the cause is gone, while a 500 cannot.
Reason phrase
Internal Server Error
Class
5xx server error
Defined by
RFC 9110
Standard
IETF standard

Tools on this site

All of these run in your browser — nothing is uploaded.

Related status codes

Codes people usually end up reading in the same session as 500.

Frequently asked questions

How do I find out which server produced this status code?

Read the response headers rather than the page. curl -sS -o /dev/null -D - https://example.com/path prints them without the body, and server, via and cf-ray between them name the layer that answered. A cf-ray value means Cloudflare handled the response and is the id their support will ask for. To take the edge out of the picture entirely, repeat the request with --resolve example.com:443:203.0.113.10, which connects to the origin address you name while still sending the original host name and SNI — if the answer changes, the edge and the origin disagree.

Does the reason phrase after the number matter?

No, and you should never branch on it. RFC 9112 tells clients to ignore the reason phrase, servers are free to change it, and HTTP/2 and HTTP/3 do not carry one at all — so 404 Not Found over HTTP/1.1 arrives as a bare 404 over HTTP/2. The phrases on this site are the registered ones because they are what people search for and what appears in an HTTP/1.1 log, not because any software depends on them.

Should I retry a request that failed with this code?

Retry 5xx and 429; do not retry 4xx, because nothing about the request will be different next time. If the response carries Retry-After, honour it — RFC 9110 defines it for exactly this, and it may be either a number of seconds or a date. Otherwise use exponential backoff with jitter and a hard cap, and only for idempotent methods: a retried POST can charge a card twice. A retry storm against a server that is already failing is how a brief incident becomes a long one.

Can I make my server return a different status code?

Yes, and the only rule that matters is that the code has to be true. Anything that reads your responses automatically — search engines, monitoring, caches, client retry logic — makes decisions from the number and never from the page. Serving an error page with 200 hides the failure from your own alerting; serving 200 for a missing page gets the error indexed as content; returning 500 for a request that was simply malformed sends whoever is on call to the wrong half of the stack.

Still stuck on 500? browse every status code in the reference — or go back up to the block written for your side of the stack.