401 Unauthorizedは「あなたが誰かわからない(認証されていない・認証情報が無効)」、403 Forbiddenは「あなたが誰かはわかったが、その操作は許可されていない」ときに返すステータスコードです。見分け方はシンプルで、ログインし直せば解決する可能性があるなら401、ログインし直しても結果が変わらないなら403です。

この記事では、HTTPの仕様(RFC 9110)に基づく401と403の定義に加えて、401で必須のヘッダー、存在を隠すために404を返す判断、そして400・404・409・422・429を含めた「どのコードを返すか」の判断フローを、ケース別の早見表で解説します。

401と403の違い

401 Unauthorized 403 Forbidden
意味 認証されていない 認可されていない(権限がない)
サーバーの認識 あなたが誰かわからない あなたが誰かはわかっている
典型例 ログインしていない、トークンの期限切れ 一般ユーザーが管理者用の機能を使おうとした
クライアントの次の行動 ログイン・トークンの再取得 再ログインしても変わらない。権限の申請など
必須のヘッダー WWW-Authenticate なし

「認証」と「認可」

この2つのコードは、認証(Authentication)と認可(Authorization)の違いに対応しています。

  • 認証 — 「あなたは誰か」を確かめること(ログイン、トークンの検証)
  • 認可 — 「あなたにその操作をする権限があるか」を判断すること

ビルの入館にたとえると、401は入口で社員証を持っていない(または期限切れの)状態、403は社員証で本人確認はできたが、そのフロアへの入室権限がない状態です。

名前が紛らわしい理由

401の名前は「Unauthorized(認可されていない)」ですが、実際の意味は「認証されていない」です。これは仕様が作られた当時の命名がそのまま残っているためで、名前ではなく定義で覚えるのが確実です。

仕様(RFC 9110)での定義

HTTPの現行の仕様であるRFC 9110では、次のように定義されています(要約)。

  • 401:リクエストに、対象のリソースに対する有効な認証情報がないため、適用されなかった。サーバーはWWW-Authenticateヘッダーを必ず含める
  • 403:サーバーはリクエストを理解したが、実行を拒否した。認証情報が提示されていた場合、サーバーはそれをアクセスに不十分と判断している
  • また、禁止されたリソースの存在自体を隠したい場合、403の代わりに404を返してもよいとされています

401にはWWW-Authenticateヘッダーが必須

401を返すときは、どの方式で認証すればよいかをWWW-Authenticateヘッダーで伝えます。

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"

Bearerトークン(OAuth 2.0のアクセストークンなど)を使うAPIでは、RFC 6750でエラーの種類の伝え方が決められています。

状況 ステータス WWW-Authenticateの例
トークンが付いていない 401 Bearer realm="api"
トークンが無効・期限切れ 401 Bearer error="invalid_token"
トークンは有効だが、スコープ(権限の範囲)が足りない 403 Bearer error="insufficient_scope"
リクエストの形式が不正 400 Bearer error="invalid_request"

「トークンは有効だが権限が足りない」は403です。この表は、401と403の線引きをそのまま表しています。

なお、WWW-Authenticate: Basic を返すと、ブラウザはユーザー名とパスワードの入力ダイアログを表示します。SPAのAPIなどでダイアログを出したくない場合は、Bearerなど別の方式を指定します。

403の代わりに404を返すという判断

権限のないユーザーに403を返すと、「そのリソースは存在する」ことが相手に伝わります。

例えば、/orders/12345 に他人がアクセスして403が返れば、「注文12345は存在する」とわかります。IDを順に変えて試すことで、存在する注文の数や番号の傾向が推測できてしまいます。

そのため、次のような場合は404を返してリソースの存在自体を隠す設計がよく採用されます。

  • 他のユーザーの注文・請求書・メッセージなど、本人以外には存在も知られたくないリソース
  • 非公開のリポジトリや、招待制のページ

大手のサービスでも、権限のない非公開リソースには404を返す設計が見られます。一方、社内の管理画面のように、存在が知られても問題なく、「権限がない」と伝えた方が利用者が迷わない場合は403が適しています。

返すコード 向いているケース
403 存在を知られても問題ない。権限がないことを利用者に伝えたい
404 存在自体を隠したい。IDの推測による情報収集を防ぎたい

どのコードを返すか:判断フロー

リクエストを受けてから、次の順に判断すると、返すコードがぶれません。

  1. リクエストの形式として読めない(JSONが壊れている、必須のパラメータがない) → 400 Bad Request
  2. 認証情報がない・無効・期限切れ → 401 Unauthorized(WWW-Authenticateを付ける)
  3. 対象のリソースが存在しない、または存在を隠したい → 404 Not Found
  4. 認証はできたが、その操作の権限がない → 403 Forbidden
  5. 形式は正しいが、内容が業務ルールに合わない(メールアドレスの形式違反、在庫数を超える注文) → 422 Unprocessable Content(または400)
  6. 現在の状態と衝突する(登録済みのメールアドレス、他の人が先に更新した) → 409 Conflict
  7. リクエストが多すぎる → 429 Too Many Requests(Retry-Afterを付ける)

認証(2)を存在確認(3)より先に判定するのは、ログインしていない人に、リソースの有無を教えないためです。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「401と403の違い|HTTPステータスコードの使い分け判断フロー」

ケース別の早見表

ケース 返すコード 補足
ログインしていない状態でマイページのAPIを呼んだ 401
アクセストークンの有効期限が切れた 401 クライアントはリフレッシュトークンで再取得する
署名が改ざんされたトークン 401
一般ユーザーが管理者用APIを呼んだ 403
他人の注文詳細にアクセスした 404(または403) 存在を隠すなら404
退会済み・停止中のアカウントでアクセスした 403 認証は通るが利用が許可されていない
無料プランのユーザーが有料機能を使おうとした 403 402 Payment Requiredは将来のための予約で、一般的には使わない
IPアドレス制限で拒否した 403
CSRFトークンが不正 403 フレームワークによっては独自のコードを使う
JSONの構文が壊れている 400
メールアドレスの形式が不正 422または400 プロジェクトでどちらかに統一する
同じメールアドレスで既に登録済み 409
楽観ロックで更新が競合した 409(If-Match使用時は412)
存在しないURL 404
URLは存在するが、そのHTTPメソッドに対応していない 405 Allowヘッダーで対応メソッドを伝える
Content-Typeが対応外 415
短時間にリクエストが集中した 429 Retry-Afterで再試行までの秒数を伝える

400と422の使い分け

400と422は、プロジェクトによって使い方が分かれるコードです。

  • 400 Bad Request — リクエストが構文として正しくない(JSONが壊れている、型が違う)
  • 422 Unprocessable Content — 構文は正しく読めたが、内容が処理できない(バリデーションエラー)

422は、以前はWebDAVの拡張でしたが、2022年のRFC 9110でHTTPの標準に取り込まれました。Ruby on RailsやLaravelなどのフレームワークは、バリデーションエラーに422を使います。一方、すべての入力エラーを400にまとめる設計も広く使われており、どちらかが誤りというわけではありません。

大切なのは、プロジェクト内で統一し、API仕様書に明記することです。

409 Conflictを使う場面

409は、リクエスト自体は正しいが、サーバー上の現在の状態と矛盾するときに使います。

  • ユニークであるべき値(メールアドレス・ユーザー名)の重複登録
  • 楽観ロックで、他の人が先に更新していた
  • 「発送済みの注文をキャンセルする」など、状態の遷移として許されない操作

クライアントは、409を受け取ったら最新の状態を取得し直してから、再度操作するかを判断します。

エラーレスポンスの本文:Problem Details

ステータスコードだけでは、「何が問題だったか」を細かく伝えられません。エラーの詳細を返す標準形式として、RFC 9457(Problem Details for HTTP APIs)があります。

HTTP/1.1 403 Forbidden
Content-Type: application/problem+json

{
  "type": "https://example.com/problems/insufficient-permission",
  "title": "この操作を行う権限がありません",
  "status": 403,
  "detail": "請求書の削除には管理者権限が必要です",
  "instance": "/invoices/12345"
}

エラーの形式をAPI全体で統一しておくと、クライアント側のエラー処理を共通化できます。ただし、401や404の本文に、内部の情報(存在するユーザー名やスタックトレースなど)を含めないよう注意します。

クライアント側での扱い方

フロントエンドやアプリでは、401と403で次の行動を変えるのがポイントです。

受け取ったコード クライアントの動き
401 リフレッシュトークンでアクセストークンを再取得し、1回だけリトライ。失敗したらログイン画面へ
403 リトライしない。「権限がありません」と表示する
404 「見つかりません」と表示する
409 最新の状態を再取得し、利用者に確認する
429 Retry-Afterの秒数を待ってからリトライする

よくある不具合が、403を受け取ったときにログイン画面へ戻してしまうことです。権限がない状態は再ログインしても変わらないため、ログインと403を繰り返すループになります。再ログインで解決するのは401だけ、と覚えておきます。

よくある質問

Q. ログインページへのリダイレクト(302)と401はどう使い分けますか? A. ブラウザで画面を表示するWebページでは、未ログイン時にログインページへ302でリダイレクトするのが一般的です。JSONを返すAPIでは、リダイレクトではなく401を返し、クライアント側でログイン画面へ遷移させます。

Q. パスワードを間違えたときは401ですか? A. ログインAPI(認証情報を受け取って検証するAPI)で認証に失敗した場合、401を返すのが一般的です。ただし、「ユーザーが存在しない」と「パスワードが違う」でメッセージを分けると、登録済みのユーザーを推測されるため、同じメッセージにします。

Q. エラーでも200を返し、本文でエラーを伝える設計はだめですか? A. 動作はしますが、ブラウザ・CDN・監視ツール・HTTPクライアントのライブラリは、ステータスコードで成功と失敗を判断します。エラーを200で返すと、キャッシュされたり、監視でエラーとして検知されなかったりする原因になるため、適切なステータスコードを返すのが基本です。

Q. 403と404のどちらを返すか、チーム内で意見が分かれます。 A. 「そのリソースの存在を知られて困るか」で判断します。個人のデータなど知られて困るものは404、社内の管理機能など存在が知られても問題なく、権限がないことを伝えた方が親切なものは403が向いています。

まとめ

  • 401は「誰かわからない」、403は「誰かはわかるが権限がない」。再ログインで解決しうるなら401
  • 401にはWWW-Authenticateヘッダーが必須。Bearerトークンのスコープ不足は403
  • リソースの存在を隠したい場合は404を返してよい(RFC 9110で認められている)
  • 判断は 400 → 401 → 404 → 403 → 422 → 409 → 429 の順で考えるとぶれない
  • 400と422はプロジェクトで統一し、エラー本文はProblem Details(RFC 9457)で揃える
  • クライアントは401だけ再認証、403はリトライしない

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「401と403の違い|HTTPステータスコードの使い分け判断フロー」

株式会社RJCが携わる保険・金融・決済のシステムでは、「誰が、何を見てよいか」の設計がお客様の安心に直結します。ステータスコード1つにも理由を持たせる設計を、チームで共有しながら開発しています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。未経験からエンジニアに育てるのが得意な会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?