Foundations roadmap

HTTP Requests and Status Codes

Log in to save this

Saving keeps this in your list across devices. It's a free account — no card.

Every time your app talks to a server, it sends an HTTP request and gets back an HTTP response. That exchange is the most common thing you will debug as a web developer. If you can read one request and its response, you can usually tell within a minute whether the bug is in your frontend, your backend, or somewhere in between.

The parts of a request

A request has four parts:

  • Method: what you want to do, such as GET or POST.
  • URL: which thing you are talking about, for example https://api.example.com/tasks?done=false. The part after ? is the query string.
  • Headers: extra information as name/value pairs, such as Content-Type: application/json (what format the body is in) or Authorization (who you are).
  • Body: the data you are sending. Many requests have none.

The response has the same shape without a method: a status code, headers, and a body. Here is a request sent from JavaScript:

const res = await fetch('https://api.example.com/tasks', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ title: 'Buy milk' }),
});
console.log(res.status); // e.g. 201
const task = await res.json();

The Content-Type header matters. Many servers only parse a JSON body when that header says application/json. Leave it out and the server may see an empty body, even though you sent data.

GET reads, POST sends

GET asks for something and should not change anything on the server. Its inputs go in the URL, like /search?q=shoes. Because a GET is safe, browsers, link previewers and caches feel free to send GETs on their own, and a user can bookmark or share the URL.

POST sends data for the server to act on: create an account, place an order, upload a file. Its data goes in the body.

The practical rule: never make a GET that deletes or changes data. A link like /delete-task?id=7 can be triggered by something as innocent as a browser prefetching links on a page.

Status code families

The first digit of the status code tells you where to look:

  • 2xx, success. 200 OK, 201 Created (something new was made), 204 No Content (it worked, nothing to send back).
  • 3xx, go elsewhere. 301 and 302 redirect to another URL, which the browser follows automatically. 304 Not Modified means "use your cached copy".
  • 4xx, the request was the problem. 400 Bad Request (malformed or invalid data), 401 (not signed in), 403 (signed in but not allowed), 404 Not Found, 429 (too many requests).
  • 5xx, the server failed. 500 means the server code threw an error. 502 or 503 usually mean the server behind a proxy is down or overloaded.

A 4xx means: change what you are sending. A 5xx means: go read the server's logs. And if there is no status code at all, the request never got an answer, for example because the domain did not resolve or the connection was refused.

fetch does not fail on a 404

A trap that catches nearly everyone: fetch only rejects when there is no response at all (a network failure). A 404 or a 500 is still a response, so the promise resolves normally. You have to check it yourself:

const res = await fetch('/api/tasks/42');
if (!res.ok) {
  // res.ok is true only for 2xx statuses
  throw new Error('Request failed with status ' + res.status);
}
const task = await res.json();

Without that check, your code happily tries to use an error page as data.

Reading a failing request in the Network tab

Open developer tools (F12), choose the Network tab, and repeat the action that fails. Click the request and check, in this order:

  1. Status. 4xx or 5xx? That already tells you which side to suspect.
  2. Headers. Is the URL exactly what you expected? Is Content-Type set? Is the Authorization header there?
  3. Payload. Did the body you meant to send actually go out, with the field names the server expects?
  4. Response. Servers often explain a 400 in the body, such as {"error": "email is required"}.

Try it

Open the Network tab on any site that has a search box and run a search. Find the request, note its method, URL, query string and status. Then change the URL to a path that does not exist and find the 404. A common mistake to avoid: seeing the page break and editing the frontend before checking the status code. A 500 is almost never fixed in the browser.

Resources

Curated resources for this node are on the way. Use what you already know how to search for, and check back soon.