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.
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
200with{"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
200and a "Page not found" screen. That's a soft 404. - A login wall, consent page or captcha is served with
200in place of the requested content, so a CDN caches it and crawlers index it. - A captive portal or corporate proxy returns its own
200HTML page in place of your API's JSON, andresponse.json()throwsUnexpected token '<'. - GraphQL APIs often return
200with anerrorsarray. 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-pageReturn 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-8for 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
Locationheader. 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
201Created
The request succeeded and created one or more new resources.
204No Content
The request succeeded and there is intentionally no content in the response.
304Not Modified
The client's cached copy is still valid, so no body is sent; reuse the cache.
404Not Found
The server has no current representation of the target resource, or won't say whether one exists.
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
HTTP Status Codes for REST APIs: 200 vs 201 vs 204, 400 vs 422 vs 429
A practical guide to choosing the right HTTP status code in REST API design. Covers the most confusing pairs: 200 vs 201, 400 vs 422, 401 vs 403, and when to use 503 vs 429.
REST API Design Best Practices: A Practical Guide
The conventions that make REST APIs intuitive and consistent. Covers URL design, HTTP methods, status codes, versioning, pagination, and error responses.
CORS Explained: Why It Happens and How to Fix It
CORS errors happen when a browser blocks a request to a different origin. Learn how the Same-Origin Policy works, what preflight requests are, and how to configure CORS correctly in Express, FastAPI, and Nginx.
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.