What do HTTP status codes like 404 and 500 mean?
In one sentence:An HTTP status code is the three-digit number a server sends back with each response, telling you in one glance whether the request worked, got redirected, or whose fault it was if it failed.
What it actually is
An HTTP status code is the three-digit number a server attaches to each response it sends, success or failure. You rarely see the number directly (your app renders a page, or an error, instead), but it’s on every response, and the first digit alone sorts it into one of a handful of families.
Think of it as the tracking update your parcel gets. “Delivered” means the request landed exactly as asked (2xx, success). “Forwarded to a new address” means what you asked for has moved and the response says where (3xx, redirection). “Address doesn’t exist” or “recipient refused it” means something about how you addressed the request was wrong (4xx, a mistake on your side). “Truck broke down before delivery” means the address was fine, but whoever was supposed to handle it failed to (5xx, a mistake on their side). A fifth family, 1xx, shows up mid-delivery rather than at the end, and you’ll rarely meet it directly.
Why your AI just did this
Your agent runs into status codes constantly, since every fetch or API call your Lovable, Cursor, Bolt, or Claude Code project makes returns one. When it wires up your own API endpoint, or calls someone else’s, that number decides what happens next: render the data, redirect, or show an error.
The part that catches beginners is that fetch() doesn’t throw just because a server sent back a bad status. Per MDN, the fetch promise only rejects on an actual network failure, like a malformed URL or no connection at all; a 404 or a 500 still counts as a successful fetch as far as JavaScript is concerned. Code that only checks for a thrown error can miss a failed request and quietly do nothing. Careful code checks response.ok (true only for status 200 to 299) or response.status directly, and plenty of AI-generated code skips that the first time, since the happy path worked fine in a quick test.
You’ll also see codes multiply once your project talks to more than one server: your frontend on Vercel calling your own API endpoint, which calls Supabase or Stripe in turn. Each hop can hand back its own code, so a 500 in your browser might have started as a crash three hops away.
When you’ll run into it
200 OK: the request succeeded. You’ll rarely notice this one; it’s what “working” looks like.301 Moved Permanently: the URL now lives somewhere else, and the response names the new location.400 Bad Request: the server can’t make sense of what you sent, often a malformed body or a missing field.401 Unauthorized: despite the name, per MDN this means unauthenticated. The server doesn’t know who you are yet; no valid login or token was attached.403 Forbidden: the server knows exactly who you are and refuses anyway. Re-authenticating won’t help; you lack permission for this action.404 Not Found: the URL doesn’t match anything the server recognizes, a typo in the path, or a deleted resource.429 Too Many Requests: you’re hitting a rate limit: the server understood you fine, you just asked too fast.500 Internal Server Error: your own server code crashed handling the request. This is the one you fix.502 Bad Gateway: a proxy or gateway in front of your app, like Vercel’s, got an invalid response from the server behind it. The failure is upstream, not in the gateway itself.
You’ll spot all of these fastest in your browser’s console, specifically DevTools’ Network tab, where each request lists its status code next to the URL and the response.
What to check
- Open the Network tab, not just the Console, and find the status column on the specific request that’s failing, not the error toast on screen
- For a 401 versus a 403, check whether a token or session was sent at all (401) or sent but lacking the right role (403). See authentication versus authorization for the fix in each direction
- For a 500, read your server logs, not the browser. The browser only shows that the server broke; the real error and stack trace live server-side
- For a 502, check the layer in front of your app (Vercel, a load balancer) separately from your own function. A crashed or timed-out function often shows up to you as a 502, one hop removed from the real cause
- Confirm your code checks
response.okorresponse.statusafter a fetch, not just whether the call threw, since a 404 or 500 resolves the promise like a success would - Treat a 429 as a rate limit signal: slow down or add a retry, instead of hunting for a feature that isn’t actually broken
Say it like a dev
Instead of: “the page just shows an error”
Say: “the Network tab shows a 404 on /api/users, can you check the route exists and the path matches?”
Instead of: “it says I’m not allowed in” Say: “I’m getting a 403, not a 401, so I’m logged in fine, I just don’t have permission for this action. Can you check the role check on this route?”
Instead of: “the server is broken” Say: “the request comes back as a 500, can you check the server logs for the actual error instead of guessing from the browser?”
People actually ask
“What does a 404 error mean?”
A 404 means the server looked for whatever you requested and found nothing matching it. In a browser that usually means the URL itself isn't recognized, while in an API it can also mean the endpoint is valid but the specific item you asked for doesn't exist. It usually points at a typo in the path, a deleted resource, or a route your agent never actually built.
“What's the difference between a 401 and a 403 error?”
A 401 means the server doesn't know who you are yet: no valid login or token came with the request, so authenticating would fix it. A 403 means the opposite: the server already knows exactly who you are and refuses anyway, because your account lacks permission for that specific action. Logging in again won't change a 403, since identity was never the problem.
“Why do I keep getting a 500 or a 502 error?”
A 500 means your own server code crashed while handling the request, so the fix lives in your server logs and your own code. A 502 means a proxy or gateway in front of your app, like the one Vercel runs, got an invalid response from the server behind it, so the failure sits one hop further away than your own function. Both are server-side, but you look in different places for each.
Related terms
Go deeper
Checked against
Just met this in a real session? Glim reads it in plain words.
Paste what your agent just did, a git diff, your terminal, or its summary, and Glim tells you what changed and what to check before you ship. Nothing stored.
Explain my session →