DEVESSENTIALS

HTTP 200 OK

200 is the default success code. What the body contains depends on the method: for GET it's the representation of the resource; for HEAD it's the same headers with no body; for POST it's the result of the action; for PUT, PATCH and DELETE it describes the outcome or returns the updated resource.

2xx · SuccessRFC 9110 §15.3.1

200 OK

The request succeeded, and the response content is the result.

Who fixes it: Nobody (success). Nothing to fix, as long as 200 really is the right answer. The problems come from sending 200 for things that aren't successes.

What 200 means

A 200 is cacheable by default: when there are no explicit freshness headers, caches may store it heuristically (RFC 9111). So a CDN can cache an error page served as 200 exactly as it would cache real content.

If the response has no body on purpose, 204 is more precise. If the request created something, 201 is.

Source: RFC 9110 §15.3.1.

When a 200 is actually a problem

  • An API returns 200 with {"success": false} or {"error": "..."}. Monitoring, retry libraries, caches and SDKs all treat it as a success.
  • A single-page app or catch-all route answers unknown URLs with 200 and a "Page not found" screen. That's a soft 404.
  • A login wall, consent page or captcha is served with 200 in place of the requested content, so a CDN caches it and crawlers index it.
  • A captive portal or corporate proxy returns its own 200 HTML page in place of your API's JSON, and response.json() throws Unexpected token '<'.
  • GraphQL APIs often return 200 with an errors array. That's normal for GraphQL, but your client has to check the array, not just the status.

How to check and fix it

Check the real status code

curl -I sends a HEAD request and prints the status line and headers. Some apps handle HEAD differently from GET, so use -w '%{http_code}' to get the status of a real GET.

# Status line and headers (HEAD request)
curl -sI https://example.com/missing-page

# Status code of a real GET, following redirects
curl -s -o /dev/null -L -w '%{http_code}\n' https://example.com/missing-page

Return real error codes from your API (Express)

Let the status code carry the outcome and the body carry the details. res.json() defaults to 200, so set the status explicitly for every other case.

app.get('/api/orders/:id', async (req, res) => {
  const order = await db.orders.find(req.params.id);
  if (!order) {
    return res.status(404).json({ error: 'order_not_found' });
  }
  res.json(order); // 200 is the default
});

Turn soft 404s into real 404s (Next.js)

Call notFound() when the data is missing so the page renders not-found.tsx with a 404 status. Call it before anything streams: once a streamed response has started, the status is already 200 and Next.js can only add a noindex meta tag.

import { notFound } from 'next/navigation';

export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params;
  const post = await getPost(slug);
  if (!post) notFound(); // 404, renders app/not-found.tsx
  return <Article post={post} />;
}

Headers that matter with 200

Cache-Control
Decide caching explicitly. Without it, caches may store a 200 heuristically, often for 10% of the time since Last-Modified.
ETag / Last-Modified
Let clients revalidate with a conditional request and get a bodiless 304 instead of downloading the whole thing again.
Content-Type
application/json; charset=utf-8 for APIs. A wrong type makes browsers and clients misinterpret a perfectly good 200 body.

200 vs similar status codes

  • 201 Created: Use 201 when a POST (or PUT) created a new resource, plus a Location header. A 200 still works, but the client can't tell anything was created.
  • 204 No Content: Use 204 when the success has no body. A 200 with an empty body leaves the client guessing whether to parse it.
  • 304 Not Modified: A revalidated cache hit. Browsers turn a 304 into the cached 200 before your JavaScript sees it, so fetch() reports 200 even when the network returned 304.

SEO impact of 200

Search engines only index URLs that return 200, or that redirect to one. A 200 on a page that says "not found" or is nearly empty is reported as a soft 404 in Google Search Console and wastes crawl budget. Return a real 404 or 410 for missing content.

Related status codes

Looking for another code? Search the full HTTP status codes list, or read which code your API should return in the REST API status codes guide.

Learn more

Browse all developer guides →

Frequently Asked Questions

Does HTTP 200 mean the request was successful?▾

Yes, from the server's point of view: it received, understood and processed the request. It doesn't guarantee the body is what you wanted, because some APIs and proxies wrongly send error messages with 200. Validate the body you parse.

What is the difference between 200 and 200 (from disk cache) in Chrome DevTools?▾

"200 (from memory cache)" or "(from disk cache)" means the browser didn't contact the server at all, because its cached copy was still fresh. A plain 200 came from the network. A 304 means the browser asked the server and was told its cached copy was still valid.

Should a POST return 200 or 201?▾

201 Created if the POST created a resource with its own URL, with a Location header pointing to it. 200 if the POST performed an action and returns its result, such as a search, a calculation or a login. 202 Accepted if the work was queued for later.