広告が嫌いですか? 行く 広告なし 今日

HTTP ステータス コード 404 か 410 か 301 をいつ返すべきか(推測をやめましょう)

更新日

Backend developers get HTTP status codes wrong all the time. Here’s a practical guide to the codes that actually matter in REST APIs — 404 vs 410, 301 vs 302, 429 rate limiting, and the 200-with-an-error-body anti-pattern.

HTTP Status Codes: When to Return 404 vs 410 vs 301 (and Stop Guessing) 1

You returned 200 with {"error": "user not found"} in the body. Your 301s are actually 302s. Your deleted resources keep returning 404 forever. Let’s go through the status codes developers get wrong most often, and what to return instead.

4xx vs 5xx: Not the Same Bucket

The most fundamental split in HTTP status codes: 4xx is the client’s fault, 5xx is yours. Simple in theory, violated constantly in practice.

The most common offender: returning 500 for a validation error. A user sent malformed JSON to your API. That’s a 400 Bad Request — their payload was wrong. A 500 tells the client “we crashed,” which triggers alerts, gets logged as a server error, and causes automated systems to retry (making your bad day worse). If the client sent garbage, say so with a 4xx.

404 vs 410: The Deleted Resource Problem

404 Not Found means: “I don’t have that right now, but maybe try again later.” Crawlers — Googlebot included — treat 404 as temporary. They’ll keep rechecking indefinitely.

410 Gone means: “That resource is permanently deleted. Stop asking.” Googlebot removes 410 URLs from the index faster than 404s, and it stops crawling them sooner. This matters for SEO and for crawl budget on large sites.

The practical rule: if your API hard-deletes a record and it’s never coming back, return 410 on subsequent requests. The only reason to prefer 404 for a deleted resource is if it might be restored — soft-delete with a recovery window, a published post that could be republished, that kind of thing. Otherwise, 410 is more honest and search engines will thank you for it.

301 vs 302 (and Why 307/308 Exist)

301 Moved Permanently: This URL is gone forever, use the new one. Browsers cache 301s aggressively. Search engines transfer link equity to the destination. If you’re consolidating domains or permanently moving a site, 301 is what you want.

302 Found: Go to this other URL for now, but come back to the original later. No caching. No link equity transfer. Use it for login redirects, temporary maintenance pages, or A/B tests.

The failure mode: using 302 for permanent moves. You think “same thing, just different,” but search engines don’t pass link equity through 302s. Years of SEO work stays attached to the old URL while the new one ranks for nothing. If you moved something permanently, use 301.

Then there’s 307 Temporary Redirect308 Permanent Redirect. These are method-safe variants: where a browser might downgrade a POST to a GET when following a 301/302, 307/308 preserve the original HTTP method. If you’re redirecting API endpoints that accept POST bodies, 307 (temporary) or 308 (permanent) keeps the method intact.

コードタイプCaches?Preserves method?Use for
301PermanentはいNo (may downgrade to GET)Permanent site moves, URL consolidation
302TemporaryいいえNo (may downgrade to GET)Login redirects, temporary unavailability
307TemporaryいいえはいTemporary redirect for API POST endpoints
308PermanentはいはいPermanent redirect for API POST endpoints

401 vs 403: Authentication vs Authorization

The naming is confusing. 401 Unauthorized actually means unauthenticated — you’re not logged in, or your credentials are missing or invalid. 403 Forbidden means you’re authenticated, but you don’t have permission to access this resource.

The practical difference: a 401 should prompt a login screen or a token refresh. A 403 should show an access-denied message. Return the wrong one and your client doesn’t know whether to ask for credentials or give up.

There’s a security dimension too. Some APIs return 404 instead of 403 for resources the caller can’t access, to avoid leaking that the resource exists. If you return 403 on a resource from another tenant, you’ve confirmed it exists. Returning 404 is more information-secure, though it makes debugging harder for legitimate users. Pick whichever tradeoff fits your threat model and apply it consistently.

429: Rate Limiting, With the Header That Actually Helps

A bare 429 Too Many Requests is annoying. A 429 with a Retry-After header is actually useful.

HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1746374400

Retry-After can be a number of seconds or an HTTP date. Well-behaved clients will back off automatically. Without it, you’re returning an error code and hoping the client figures out when to retry — most will just hammer your endpoint again immediately, which defeats the purpose of rate limiting.

Don’t return 503 for rate limiting. 503 is “service unavailable” — infrastructure down, deployment in progress, circuit breaker open. Rate limiting is a policy decision, not a service failure. 429 exists exactly for this.

The “200 With an Error Body” Anti-Pattern

This one keeps appearing in production APIs and it needs to stop:

HTTP/1.1 200 OK
Content-Type: application/json

{"status": "error", "message": "user not found"}

This breaks everything that depends on HTTP semantics. Monitoring tools report the request as successful. Load balancers pass it through. Client libraries don’t throw. Logs look clean while users see errors. The fix is a one-liner: return the right status code. 200 means it worked. If it didn’t work, don’t return 200.

The same logic applies to 201 vs 200 for resource creation. If a POST to /users creates a new user, return 201 Created で包まれており、 Location header pointing to the new resource. Return 200 and you’ve thrown away information your clients could use to avoid an extra round-trip.

API Scenario → Correct Status Code

Here’s a reference table for the scenarios that come up most in REST API development:

シナリオStatus code注記
Request body is malformed JSON400 Bad RequestInclude a message pointing to the parse error
Missing or invalid field in request422 Unprocessable Entity422 is more precise than 400 for semantic validation failures
Auth token missing or expired401 UnauthorizedInclude WWW-Authenticate header
Authenticated but no permission403 ForbiddenOr 404 if hiding resource existence is a requirement
Resource not found, might return404 Not FoundFor temporary absence or unknown resources
Resource permanently deleted410 GoneCrawlers will stop rechecking sooner
Created a new resource201 Created追加する Location: /resources/new-id header
DELETE succeeded, no body204 No ContentDon’t return an empty JSON object with 200
Permanent URL change301 Moved PermanentlySearch engines transfer link equity
Temporary redirect302 FoundNo caching, no equity transfer
Rate limit exceeded429 Too Many RequestsAlways include Retry-After
Unexpected server exception500 Internal Server ErrorDon’t leak stack traces in the body
Upstream dependency is down503 Service Unavailable追加する Retry-After if downtime is bounded
POST redirect, preserve method307 or 308307 temporary, 308 permanent

When you’re mid-PR and need to verify what a status code actually means, the HTTP Status Codes Lookup tool gives you the spec definition, typical use cases, and which RFC defines it.

Checking Your Redirect Chains

After a site migration or URL restructure, it’s worth verifying the full redirect chain — especially if you’ve got 301s that were later updated to point somewhere else, creating multi-hop chains that bleed link equity. The リダイレクトチェッカー traces the chain and shows each status code in sequence, so you can confirm everything resolves in one hop.

広告なしで楽しみたいですか? 今すぐ広告なしで

拡張機能をインストールする

お気に入りのブラウザにIOツールを追加して、すぐにアクセスし、検索を高速化します。

に追加 Chrome拡張機能 に追加 エッジ拡張 に追加 Firefox 拡張機能 に追加 Opera 拡張機能

スコアボードが到着しました!

スコアボード ゲームを追跡する楽しい方法です。すべてのデータはブラウザに保存されます。さらに多くの機能がまもなく登場します!

ニュースコーナー 技術ハイライト付き

参加する

価値ある無料ツールの提供を継続するためにご協力ください

コーヒーを買って