HTTP : リダイレクトのステータスコードはなぜ分かりにくいのか

現在、URL リダイレクト (URL redirection) を用途とする主な HTTP ステータスコードは 301, 302, 303, 307, 308 の 5 つです。

これらのステータスコードについて私のまわりのエンジニアに話を聞いてみたところ、リダイレクト処理を書こうとする都度、どのステータスコードが何だったかや、細かい仕様の違いについて調べる方が多いようです。私も常々、リダイレクト系のステータスコードは、わかりづらく、憶えにくいと感じていました。そしてこの原因は、ステータスコード定義の変遷にあると考えています。

この記事は、この「わかりづらく、憶えにくい」というストレスの軽減を目的に、ステータスコード定義の歴史について RFC からひも解いてみようという個人的な試みの記録です。

HTTP ステータスコードの歴史

まずは背景を知る上で、ステータスコードに関する RFC 策定の流れを整理します。

今回扱うのは HTTP/1.1 までです。

恒久リダイレクト

恒久リダイレクトに関する仕様策定、変更の流れを追ってみます。

1996 年に出された RFC1945 の定義を読むと、301 Moved Permanently についてこう書かれています。

9.3 Redirection 3xx - 301 Moved Permanently (RFC1945)

If the 301 status code is received in response to a request using the POST method, the user agent must not automatically redirect the request unless it can be confirmed by the user, since this might change the conditions under which the request was issued.

セキュリティ面の配慮だと思いますが、POST でのリクエストに対する 301 レスポンスは、ユーザーエージェント側で自動的にリダイレクトしないよう決められています。これはリダイレクト時の再 POST に関する制限と読めるので、301 Moved Permanently はリダイレクト時のリクエストメソッドを変更しないことを想定しているとうかがえます。

注釈を見ると、既存ユーザーエージェントのいくつかは、POST に対するリダイレクトを「誤って」 GET に変更してしまうという状況についても記載されています。

9.3 Redirection 3xx - 301 Moved Permanently (RFC1945)

Note: When automatically redirecting a POST request after receiving a 301 status code, some existing user agents will erroneously change it into a GET request.

この状況を踏まえ、2014 年の RFC7231 にて 301 Moved Permanently は POST から GET へのリクエストメソッド変更が許容されるようになりました。

6.4.2. 301 Moved Permanently (RFC7231)

Note: For historical reasons, a user agent MAY change the request method from POST to GET for the subsequent request. If this behavior is undesired, the 307 Temporary Redirect) status code can be used instead.

また、ここにはリクエストメソッドを変更しないリダイレクトについて、1999 年の RFC2616 で追加された一時リダイレクトである 307 Temporary Redirect を使って良いと書かれていますが、 2014 年の RFC7238 で 308 Permanent Redirect が追加されています。

一時リダイレクト

一時リダイレクトについても辿ってみます。

まず 1996 年に出された RFC1945 で 302 Moved Temporarily が定義されていますが、301 Moved Permanently と同様、当初はリクエストメソッドの変更を許可していません。

しかし 1999 年 RFC2616 になると、302 の Resason-Phrase が Found に変更されます。仕様について大きく変更された訳ではありませんが、同 RFC2616 にて追加された 307 Temporary Redirect と、少し遡って 1997 年の RFC2068 で追加された 303 See Other を使い分けることで、役割を明確にしようとしています。

10.3.3 302 Found (RFC2616)

Note: RFC 1945 and RFC 2068 specify that the client is not allowed to change the method on the redirected request. However, most existing user agent implementations treat 302 as if it were a 303 response, performing a GET on the Location field-value regardless of the original request method. The status codes 303 and 307 have been added for servers that wish to make unambiguously clear which kind of reaction is expected of the client.

301 Moved Permanently に対する 308 Permanent Redirect と同様、307 Temporary Redirect はリクエストメソッド変更を許可しない仕様になっており、2014 年の RFC7231 にて 302 Found も 301 Moved Permanently と同様に POST から GET へのリクエストメソッド変更が許容されるようになりました。

303 See Other とは何なのか

302 Found と 307 Temporary Redirect の役割の違いは明確で、一時リダイレクトに対し、リクエストメソッド変更を許容するか否かです。

303 See Other については、RFC7231 内にこう書かれています。

6.4.4. 303 See Other (RFC7231)

The 303 (See Other) status code indicates that the server is redirecting the user agent to a different resource, as indicated by a URI in the Location header field, which is intended to provide an indirect response to the original request.

ここで注目したいのは、303 See Other が、302 Moved Temporarily から分離されたステータスコードであるにも関わらず、定義文から "temporarily" という単語が消えていることです。303 See Other は一時リダイレクトを扱うものではないということです。比較のため、302 Found と 307 Temporary Redirect の定義文を引用しますが、次の通り "temporarily" という単語が使われています。

6.4.3. 302 Found (RFC7231)

The 302 (Found) status code indicates that the target resource resides temporarily under a different URI.

6.4.7. 307 Temporary Redirect (RFC7231)

The 307 (Temporary Redirect) status code indicates that the target resource resides temporarily under a different URI and the user agent MUST NOT change the request method if it performs an automatic redirection to that URI.

更に、303 See Other については RFC2616, RFC7231 に次のような記載もあり、リダイレクト時に GET や HEAD を使うことが想定されていることもわかります。

10.3.4 303 See Other (RFC2616)

The response to the request can be found under a different URI and SHOULD be retrieved using a GET method on that resource.

6.4.4. 303 See Other (RFC7231)

A user agent can perform a retrieval request targeting that URI (a GET or HEAD request if using HTTP), which might also be redirected, and present the eventual result as an answer to the original request.

これらの内容から、303 See Other, 307 Temporary Redirect が追加される前の 302 Moved Temporarily の利用実態は次のようなものだったと考えられます。

  1. 既存のリソースに対し、一時的に別の URI を割り当てる
  2. リクエストに対する結果として、適切な別の URI に誘導する

1 の用途をリダイレクト時のリクエストメソッドの変更可否で分けたのが 302 Found と 307 Temporary Redirect であり、2 のような用途が 303 See Other です。

例えばフォーム送信後、完了ページへは GET でリダイレクトするようなケースが 303 See Other です。他にも GET でのリクエスト時にサーバー側でユーザーのロケールを判定し、ロケールにあった言語コンテンツのある URI にリダイレクトするようなケースもそうでしょうか。

まとめ

ステータスコード別の特徴については、英語版 Wikipedia の URL redirection ページに記載されている Redirect status codes and characteristics がわかりやすいので、そちらを参考に本記事をまとめると次のようになります。

ステータスコード 一時/恒久 メソッド 登場年 RFC
301 Moved Permanently 恒久 変更可 1996 年 RFC1945 RFC2068 RFC2616
302 Found 一時 変更可 1996 年 RFC1945 RFC2068 RFC2616
303 See Other 記載なし GET or HEAD 1997 年 RFC2068 RFC2616 RFC7231
307 Temporary Redirect 一時 変更不可 1999 年 RFC2068 RFC2616 RFC7231
308 Permanent Redirect 恒久 変更不可 2014 年 RFC7238 RFC7538

最後に

この記事を書いたおかげで、狙い通りストレスが解消された気がします。しかし私の経験では、残念ながら「対象リソースが一時的に別 URI に属す (the target resource resides temporarily under a different URI) 」という用途でリダイレクトを使ったことはありません……。

Comments are closed, but you can leave a trackback: Trackback URL.