HTTP 401 と 403 の違い・使い分け
レビューでよく指摘されるのが 401 Unauthorized と 403 Forbidden の 取り違えです。RFC 9110 の定義に沿えば迷いません。
The canonical rule: 401 means "who are you?" (authentication), 403 means "I know who you are, but you may not" (authorization).
TL;DR
- 401: 認証されていない / 認証失敗。
WWW-Authenticateヘッダ必須 - 403: 認証済みだが権限なし。再認証しても解決しない
- リソースの存在を隠したい場合は 404 を返す
1. 正しい使い分け表 / When to use
| 状況 / Situation | 返すコード / Status | 補足 / Notes |
|---|---|---|
| ログインしていない | 401 | WWW-Authenticate: Bearer |
| トークン期限切れ | 401 | error="invalid_token" |
| トークン署名不正 | 401 | 認証自体が失敗 |
| ログイン済み・権限不足 | 403 | ロール外のリソース |
| 料金プラン未加入 | 403 (または 402) | 402 は実質未定着 |
| private リポジトリ | 404 | 存在を隠す設計 |
2. RFC 9110 の定義 / RFC definition
一次情報は RFC 9110 の401 Unauthorized(15.5.2節)と403 Forbidden(15.5.4節)です。実装やレビューで判断が割れたときは、この定義に戻ると整理できます。
401 Unauthorized: "the request has not been applied because it lacks valid authentication credentials for the target resource."
403 Forbidden: "the server understood the request but refuses to authorize it."
401 では認証方式を示すため WWW-Authenticate を 必ず付ける義務があります。 付けないと仕様違反です。
3. 実装例 / Examples
Express (Node.js)
// 401 — 認証必要
res.set("WWW-Authenticate", 'Bearer realm="api"').status(401).json({ error: "unauthenticated" });
// 403 — 権限なし
res.status(403).json({ error: "forbidden", reason: "role:admin required" });FastAPI (Python)
from fastapi import HTTPException
raise HTTPException(
status_code=401,
detail="Invalid token",
headers={"WWW-Authenticate": "Bearer"},
)4. 間違いやすいパターン / Anti-patterns
- 403 に
WWW-Authenticateを付ける(RFC 上は 401 用) - トークン期限切れで 403 を返す(再ログインで解決するので 401 が正)
- ブラウザのログインダイアログを避けるためにすべて 403 にする(クライアントのリトライ戦略が崩れる)
401 に対して無条件リトライする
期限切れや無効なトークンを付けたまま同じリクエストを繰り返しても、認証状態は変わりません。サーバー負荷を増やすだけでなく、レート制限やアカウント保護に引っかかる原因にもなります。401 を受けたクライアントはリトライを止め、保存済み認証情報を破棄してログイン画面へ誘導します。
const response = await fetch("/api/profile", { headers: authHeaders });
if (response.status === 401) {
localStorage.removeItem("accessToken");
window.location.assign("/login?reason=session-expired");
return;
}
if (response.status === 403) {
showMessage("この操作を行う権限がありません");
}403 は再ログインで直るとは限らないため、必要なロールや申請方法を案内するほうが適切です。401 と 403 を分けることは、HTTP上の正しさだけでなくユーザー体験にも直結します。
5. よくある質問 / FAQ
期限切れトークンには 401 と 403 のどちらを返す?
有効な認証資格情報がない状態なので 401 です。Bearer 認証では WWW-Authenticate に認証方式やエラー情報を示し、クライアントが再ログインやトークン更新を判断できるようにします。
ログイン済みでも 401 を返すことはある?
送信されたトークンが失効・改ざん・対象API向けでないなど、認証に失敗した場合は 401 です。「画面上でログイン済み」かではなく、今回の要求に有効な認証資格情報があるかで判断します。
403 を受けたら再ログインすべき?
通常は不要です。認証済みで権限が足りない状態なので、権限申請や別アカウントの利用を案内します。
6. English summary
Per RFC 9110, return 401when the client isn't authenticated (no token, expired token, invalid signature) — and include a WWW-Authenticate header so clients know what scheme to use. Return 403when the client is authenticated but lacks permission; re-authenticating won't help. If you need to hide the existence of a resource (private repos, internal records), return 404to avoid leaking metadata.