Skip to content

HTTP status code · Success (2xx)

200 OK

200 OK means the request succeeded and the response carries the result: the page or data for a GET, the outcome of the action for a POST. It is the status almost every framework sends by default when your handler returns without setting one.

Facts about this status code
Class2xx, Success
Defined inRFC 9110 §15.3.1
Cacheable by defaultYes, heuristically cacheable; for GET and HEAD; send ETag or Last-Modified so caches can revalidate
Safe to retryNo need; the request succeeded
Relevant headers
  • ETag: RFC 9110 says a 200 to GET or HEAD SHOULD carry validators; enables 304 later
  • Cache-Control: use private or no-store for per-user responses so shared caches do not reuse them

What 200 means

RFC 9110, section 15.3.1, ties the meaning of the body to the method: for GET it is the resource, for HEAD the same headers without the body, for POST the result of the action, for PUT and DELETE the status of the action. For CONNECT a 200 means the tunnel is open and has no body at all.

A 200 is heuristically cacheable, so a browser or CDN may reuse it without asking again unless Cache-Control says otherwise. For GET and HEAD the spec asks for validators, preferably both a strong ETag and Last-Modified, which is what lets later requests get a cheap 304 instead of the full body.

A 200 only says the HTTP exchange worked. Plenty of systems return 200 with an error inside: GraphQL servers put failures in an errors array, some APIs wrap everything in { "success": false }, and misconfigured sites serve "page not found" templates as 200. Monitoring that checks only the status will call all of those healthy.

When to use it

  • GET that returns the resource or a list, even an empty list.
  • POST that performs an action without creating an addressable resource: a search, a calculation, a login that returns a token.
  • PUT or PATCH that returns the updated resource in the body (use 204 if you return nothing).

Common causes

If you are visiting the site

  • The page shows 200 but old content: DevTools says "(from disk cache)" or "(memory cache)", so the browser never asked the server.

If you run the server

  • A blank page with status 200: the HTML shell loaded but the JavaScript bundle threw an error, so the problem is in the console, not the network.
  • An error page, empty result or failed login is returned with 200, so clients, crawlers and uptime checks treat it as success.
  • One user sees another user's data: a CDN cached a personalized 200 because it had no Cache-Control: private.

How to fix it

If you are visiting the site

  • Hard-reload with Ctrl+Shift+R (Cmd+Shift+R on macOS) to bypass the cached copy.

If you run the server

  • Return the status that matches the outcome: 404 for missing, 401 or 403 for auth failures, 422 for invalid input.
  • Mark per-user responses with Cache-Control: private, no-store, and version static assets so long max-age values are safe.
  • Point uptime checks at a health endpoint that returns 200 only when its dependencies respond.

How to send 200

Express (Node.js)
app.get('/reports/:id', (req, res) => {
  res.status(200).json({ id: '42', title: 'Q3 revenue' });
});
Next.js App Router route handler
// app/reports/[id]/route.ts
export async function GET() {
  return Response.json({ id: '42', title: 'Q3 revenue' }, { status: 200 });
}
Go net/http
mux.HandleFunc("GET /reports/{id}", func(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK) // 200
	w.Write([]byte(`{"id":"42","title":"Q3 revenue"}`))
})
Python FastAPI
from fastapi import FastAPI
from fastapi.responses import JSONResponse

app = FastAPI()

@app.get("/reports/{id}")
def get_report(id: str):
    return JSONResponse(status_code=200, content={"id": "42", "title": "Q3 revenue"})
Nginx
# Health check answered by nginx itself
location = /healthz {
  access_log off;
  default_type text/plain;
  return 200 "ok\n";
}

Commonly confused with

200 vs 201
201 Created says a new resource now exists (and where, via Location); 200 on a POST only says the action worked.
200 vs 204
204 No Content is a success with nothing in the body; a 200 is expected to carry content.
200 vs 304
304 means the browser asked and the server confirmed its cached copy is still good; "200 (from cache)" means the browser did not ask at all.

Frequently asked questions

Does 200 OK always mean success?
It means the HTTP request succeeded, not that your operation did. Some APIs and GraphQL servers return 200 with an error in the body, so check the body format your API documents as well as the status.
What does "200 (from disk cache)" mean in DevTools?
The browser reused a stored copy without contacting the server, because the cached response was still fresh under its Cache-Control or heuristic lifetime. No request left your machine.
Should a POST return 200 or 201?
Return 201 Created when the POST creates a resource with its own URL, and include Location. Return 200 when it performs an action or returns a computed result without creating anything addressable.
Can a 200 response have an empty body?
Yes, with Content-Length: 0, but RFC 9110 expects a 200 to carry content and recommends 204 No Content when there is intentionally nothing to return.

Last reviewed by Arielton Oberek.