HTTP 200 OK

OK

HTTP 200 is OK: the request succeeded, and the body carries the result. RFC 9110 section 15.3.1 ties that body to the method: the representation for GET, the same headers with no body for HEAD, the outcome of the action for POST, PUT and DELETE. A 200 is expected to have content; when there is none to send, 204 says so.

Picking the right 2xx

GET /orders/42             200  the order
POST /orders               201  Created, with Location: /orders/42
PUT /orders/42 (exists)    200  with the new state, or 204 with none
PUT /orders/43 (new)       201  Created
POST /exports              202  Accepted: queued, not done yet
DELETE /orders/42          204  No Content
GET with a Range header    206  Partial Content
GET /orders?status=lost    200  with an empty list, not 404 or 204

The PUT rows are a rule, not a style: RFC 9110 section 9.3.4 says a PUT that creates must answer 201, and one that replaces must answer 200 or 204.

The 200 with an error inside

200 {"error": "not found"} tells every cache, proxy, monitor and retry library that the request worked. A CDN may store it, an uptime check stays green, and a client that branches on response.ok takes the success path. On web pages the same mistake is what Google calls a soft 404: a “not found” template served with 200, which Search Console reports. Send the real 4xx or 5xx and put the detail in the body. GraphQL is the well-known exception: servers commonly answer 200 with an errors array, because one response can carry partial data and errors together.

Caching

200 is heuristically cacheable (RFC 9110 section 15.1): with no Cache-Control or Expires, a cache may choose its own freshness lifetime. RFC 9111 section 4.2.2 suggests no more than a fraction, typically 10%, of the time since Last-Modified, so a file last changed 30 days ago can be reused for up to about three days without asking. That’s the usual cause of “users still see the old version”. Set Cache-Control on every 200 that matters: no-cache to revalidate each time, max-age=31536000, immutable for fingerprinted assets.

Health checks

An AWS ALB health check counts only 200 as healthy unless you widen its success codes, for example to 200-299. Kubernetes HTTP probes accept any code from 200 to 399.

In your stack

Every framework sends 200 when you don’t choose:

res.json(order);                    // Express
return order                        # Flask and FastAPI; Django: return JsonResponse(order)
json.NewEncoder(w).Encode(order)    // the first Write sends 200 if WriteHeader wasn't called
location = /healthz {
    default_type text/plain;
    return 200 "ok\n";
}

Try it in the HTTP Status Codes

200 OK

  1. 200OK2xx Success

    The request worked and the response body carries the result.

    When to use it

    The ordinary answer to a successful GET, and to any POST or PUT that returns the resource it changed.

    Common mistakes

    Returning 200 with an error message in the body, or with an empty body where 204 says the same thing honestly.

    Cacheable by defaultRFC 9110

Every code, by class

Codes marked unofficial come from one server’s own convention — nginx, Cloudflare, cPanel, Microsoft — and are not in the IANA registry.

Runs in your browser. Nothing you enter is sent anywhere.