【和訳】全ての Web 開発者が知るべきプロトコル HTTP【Part.1】

株式会社オズビジョンの @terra_yucco です。

ちょっとしたご縁で知り合った方より、非常にシンプルかつ体系立ててまとめられた HTTP の説明資料をいただきました。なんとなくで知っていたことが整理される感覚を久々に味わったので、こちらを翻訳してシェアしようと思います。

# Source Article

- [HTTP: The Protocol Every Web Developer Must Know - Part 1](https://code.tutsplus.com/tutorials/http-the-protocol-every-web-developer-must-know-part-1--net-31177)

以下に記載の文章は、最後の Conclusion 部分を除き原文の内容をユッコの英語力の範囲で訳したものです。ただし、正確に一言一句訳すわけではなく、かなりの意訳を挟み、一部文章を表に整理したりしています。
貼り付けられている図表類は全て上記記事からお借りしています。

## 導入/Introduction

※ここは原文にはありません。見やすさのために見出しを入れました。

HTTP とは `Hypertext Transfer Protocol` のこと。
ステートレスで、分散したシステム間のやり取りをアプリケーション層で行うためのプロトコルで、近年の Web の基礎になっている。Web 開発者なら、このプロトコルについてはしっかり理解しておくこと。

この強力なプロトコルを Web 開発者の目線で見ていこう。この話題は大きく 2 つに分けられる。最初のエントリでは基礎的な部分と、様々な要求・応答の概要を見ていくことにして、続くエントリでより特徴的な部分－例えばキャッシュの利用や接続の制御、認証といった部分に踏み込んでいく。

ヘッダの項では少し細かい話もするけれど、深く知るには RFC ([RFC 2616](https://www.w3.org/Protocols/rfc2616/rfc2616)) を見るのがいい [^1]。ただ、この記事でも RFC の一部に言及することはある。

## HTTP の基礎/HTTP Basics

HTTP では様々なホストとクライアントの間で通信が行える。様々なネットワーク構成が混在していても対応可能。

**特定のシステムを想定せず、異なる通信の間で状態の保存を行わない**ことでこれを実現している。

そのため HTTP はステートレスなプロトコルとなる。通常は TCP/IP 上で通信するけれど、信頼できるトランスポートならどこでも使うことができる。デフォルトの TCP/IP ポートは 80 だけど、他のポートも利用可能。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95030/f8ea5bbb-8fed-b445-f645-2a7bb60e9e81.png)

ホストとクライアントの間の通信は、要求と応答のペアで行われる。クライアントが HTTP リクエストを開始し、(対応する) HTTP レスポンスを受け取ることでサービスが提供される。この基本的なメッセージペアについて、次のセクションで説明していく。

現在のこのプロトコルのバージョンは HTTP/1.1 であり、以前の 1.0 に比べるといくつかの機能が追加さえている。重要なものとしては、接続の持続、チャンクの転送コーディング、そしてきめ細かい調整が行えるキャッシュヘッダがある。これらの機能についてはこの記事では簡単にしか触れないけれど、次の記事で細かく説明していく。

### URLs

Web コミュニケーションの心臓部は要求メッセージにある。要求メッセージは Uniform Resource Locators (URLs) を通して送られる。おそらく読者は URL には詳しいだろうが、念のため、ここで説明する。
URL は以下のシンプルな構成をしている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95030/88038628-5a81-7c0d-6ace-ff6046a2cc1b.png)

プロトコル (protocol) は多くは http だが、セキュアな通信のために https を使うこともできる。デフォルトのポートは 80 だが、図に記載されている通り、明示的に指定することもできる。リソースパス (resource path) 部分は、サーバ上でのそのリソースの位置を示す。

### メソッド/Verbs [^2]

通信先するホストは URL で特定できるけれど、ホストの上で行おうとしているアクションは HTTP verb (リクエストメソッド) で指定される。クライアントがホストに実行させたいアクションは何種類かあるので、HTTP はどんなタイプのアプリケーションにも対応できるようにそれを形式化している。

GET, POST, PUT, DELETE の 4 つのリクエストメソッドはよく使われるもので、大半のツールやフレームワークがこれらに対応している。
PUT と DELETE は POST の特殊なものと考えられることもあり、実際には作成・更新・削除を含む POST としてパッケージにされていることもある。

その他、利用機会は少ないが HTTP がサポートしているものもいくつかある。

リクエストメソッド|詳細
:--|:--
GET|既存リソースの取得。サーバがリソースを取得して返却するのに必要なすべての情報が URL に含まれる
POST|新規リソースの作成。作成のためのデータは通常 payload として要求される
PUT|既存リソースの更新。更新のためのデータは通常 payload として要求される
DELETE|既存リソースの削除
HEAD|GET に似ているが、メッセージボディが存在しない。特定のサーバリソースに対するヘッダを取得するために使われるもので、リソースに更新があるかを timestamp で判断する用途が多い
TRACE|サーバまでの経路のホップを取得するために使われる。中間プロキシやゲートウェイが `Via` ヘッダに IP や DNS 名称を設定するため、診断目的で使える
OPTIONS|サーバの機能を取得する。サーバのサポートしている機能に応じてクライアントからの要求を変更したい場合などに使える

### ステータスコード/Status Codes

URL とリクエストメソッドにより、クライアントはサーバに要求を行う。その結果として、サーバはステータスコードとメッセージで応答する。ステータスコードはとても重要で、クライアントがサーバの応答をどのように解釈したらいいのかを教えてくれる。HTTP 仕様では応答の種類によって、数値の範囲を定義している。

#### 1xx: Informational Messages

1xx のコードは HTTP/1.1 で導入されているが、暫定的なもの。
サーバは `Expect: 100-continue` というヘッダを送信して、クライアントに残りの要求の送信 (既に送信済みの場合は無視) を指示することができる。HTTP/1.0 のクライアントはこのヘッダを無視する。

#### 2xx: Successful

2xx のコードは、クライアントに要求が正しく処理されたことを伝える。もっともよく使われるのは `200 OK`。`GET` の要求においては、サーバはリソースをメッセージ本文で送信する。

Code/Reason Phrase|意味
:--|:--
200 OK|【訳注】正常に要求が処理され、応答にリソースが含まれている
202 Accepted|要求は受け付けられたが、応答にリソースは含まれていない。サーバに非同期処理を指示にするのに有効。サーバによってはモニタリングのための情報を送ることもある。
204 No Content|応答にメッセージボディがない。
205 Reset Content|クライアントにドキュメントの表示をリセットするように指示するもの。
206 Partial Content|応答にコンテンツの一部しか含まれていないことを示す。ヘッダにより正確な範囲と、コンテンツの有効期限が別途表現される。

#### 3xx: Redirection

3xx のコードはクライアントに追加アクションを要求する。もっともよく使うケースとしては、リソースを得るために他の URL へジャンプさせるもの。

Code/Reason Phrase|意味
:--|:--
301 Moved Permanently|対象のリソースは新しい URL にある。
303 See Other|対象のリソースは一時的に新しい URL にある。`Location` ヘッダが新しい URL を含んでいる。
304 Not Modified|リソースは更新されていないので、クライアントはキャッシュされたコピーを使う必要がある。これはクライアントが送信する `ETag` (コンテンツのハッシュ情報) と、サーバ側で同ロジックで生成したものを比較し、更新があるかを判断して返している

#### 4xx: Client Error

4xx のコードは「クライアントからの要求が間違っている」とサーバが判断したときに返される。無効なリソースを要求したときや、正しくない要求をしたときなど。以下のようなものがある。

Code/Reason Phrase|意味
:--|:--
400 Bad Request|要求形式が正しくない
401 Unauthorized|認証が必要。クライアントは `Authorization` ヘッダをつけて要求を繰り返すことができる。クライアントが既に該当ヘッダを送っていてこのコードが返される場合には、認証情報報が正しくない。
403 Forbidden|リソースへのアクセスをサーバが拒否している
404 Not Found|リソースが無効でサーバに存在しない
405 Method Not Allowed|リクエストメソッドが正しくないか、サーバがサポートしていないもの
409 Conflict|クライアントの要求時刻より後に更新されたリソースを要求しているため、サーバは要求を完了できない。共有リソースを PUT リクエストで編集するときなどにこの状況が発生する

#### 5xx: Server Error

5xx のコードはサーバが要求を処理している最中に失敗したことを示す。以下のようなものがある。

Code/Reason Phrase|意味
:--|:--
500 Internal Server Error|内部エラーの発生
501 Not Implemented|サーバは要求された機能をサポートしていない
503 Service Unavailable|サーバ内部のシステムでエラーが発生していたり、高負荷の場合に発生する。サーバが応答しなくなりタイムアウトすることもある

### 要求・応答メッセージの形式/Request and Response Message Formats

ここまで URL・リクエストメソッド・ステータスコードといった、HTTP 要求・応答ペアにおける重要なパーツを見てきた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95030/0c566584-723b-8df4-a91f-aece860bec69.png)

ここからは実際のメッセージ内容を見ていこう。
HTTP の仕様では、要求・応答メッセージは以下のような共通した構造を持っている

```
<start-line>
*(<message-header>)
<CRLF>
[<message-body>]
```

`<start-line>` = リクエストラインもしくはステータスライン
`<message-header>` = フィールド名称 ':' フィールド値

メッセージヘッダと本文の間の `CRLF` は不可欠。
メッセージは一つ以上のヘッダを含むことができる。ヘッダには以下のような種別がある。

- 一般ヘッダ：要求・応答両方で利用されるもの [^3]
- リクエストヘッダ：リクエストでしか使わないもの [^4]
- レスポンスヘッダ：レスポンスでしか使わないものにしか使えないもの [^5]
- エンティティヘッダ [^6]

メッセージボディがエンティティデータを完全に保持していることもあるし、断片的にしか保持していない場合もある (`Transfer-Encoding: chunked` が設定されている場合、断片的な保持)。
※すべての HTTP/1.1 クライアントは `Transfer-Encoding` ヘッダを受け入れなければならない。

#### 一般ヘッダ/General Headers

要求・応答メッセージ両方で共有されるヘッダ。

ヘッダ名称|説明
:--|:--
Cache-Control|Part.2 で説明
Connection|Part.2 で説明
Date|要求・応答メッセージのタイムスタンプ
Pragma|カスタムヘッダともみなされ、実装に特化したヘッダを含む。もっともよく見かけるのは `Pragma: no-cache` だがこれは正しくは HTTP/1.1 では `Cache-Control: no-cache` とするべきもの。
Trailer|(言及無し)
Transfer-Encoding|`Transfer-Encoding: chunked` として応答を小さい単位にブレイクダウンするために使われることが多い。HTTP/1.1 で登場した新しいヘッダであり、クライアント側でのストリーミングを可能にしている
Upgrade|新しいプロトコルへの円滑な移行のために設けられており、プロトコルを切り替えるのに使われる
Via|TRACE メッセージで利用され、すべての中継プロキシ・ゲートウェイによって更新されていくヘッダ
Warning|(言及無し)

#### エンティティヘッダ/Entity headers

要求・応答メッセージは対象コンテンツに関するメタ情報を提供するためにエンティティヘッダを使うことがある。

ヘッダ名称|説明
:--|:--
Allow|(言及無し)
Content-Encoding|(※1)
Content-Language|(※1)
Content-Length|(※1)
Content-Location|(※1)
Content-MD5|(※1)
Content-Range|(※1)
Content-Type|(※1)
Expires|いつそのエンティティが有効期限切れとなるかを示す。面白いことに「有効期限無し」を示す場合には 1 年後を指定する。
Last-Modified|対象エンティティが最後に更新されたタイムスタンプ

(※1) `Content-` という接頭辞が付くヘッダは全て、構造やエンコーディング、メッセージ本文のサイズなどを示している。エンティティがメッセージの一部の場合には、これらのヘッダは必須となる場合がある。

Tips: クライアントがカスタムヘッダを作成して送ることもできる。これらも HTTP プロトコルんおいてはエンティティヘッダとして扱われれる。

これは本当に拡張性の高い仕組みで、いくつかのクライアント～サーバ実装においてはこれらの拡張ヘッダを使った通信も実装されている。
ただし、HTTP はカスタムヘッダをサポートしているものの、実際の通信においてまず必要とされるのはリクエストヘッダとレスポンスヘッダである。

#### 要求の形式/Request Format

要求メッセージはリクエストラインを除けば、上と同じような構造をしている。

```
{Method}<SP>{URI}<SP>{HTTP-Version}<CRLF>
```

- `{Method}` => いずれか `OPTIONS`,`HEAD`,`GET`,`POST`,`PUT`,`DELETE`,`TRACE`
- `<SP>` => 半角スペース
- `{HTTP-Version}` => 例えば `HTTP/1.1`

リクエストラインを含めたヘッダは、例えば以下のようになる。

```
GET /articles/http-basics HTTP/1.1
Host: www.articles.com
Connection: keep-alive
Cache-Control: no-cache
Pragma: no-cache
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
```

リクエストラインの後にはたくさんのヘッダが続く。`Host` ヘッダは HTTP/1.1 クライアントにおいては必須。`GET` リクエストはメッセージボディを持たないが、`POST` リクエストはデータをボディに持つことができる。
リクエストヘッダはリクエストメッセージの修飾子である。既定されているリクエストヘッダはそこまで多くない。不明なものはエンティティヘッダとして扱われる。

ヘッダ名称|説明
:--|:--
Accept|(※2)
Accept-Charset|(※2)
Accept-Encoding|(※2)
Accept-Language|(※2)
Authorization|(言及なし)
Expect|(言及なし)
From|(※3)
Host|(※3)
If-Match|(※4)
If-Modified-Since|(※4)
If-None-Match|(※4)
If-Range|(※4)
If-Unmodified-Since|(※4)
Max-Forwards|(言及なし)
Proxy-Authorization|(言及なし)
Range|(言及なし)
Referer|(※3)
TE|(言及なし)
User-Agent|(※3)

(※2) `Accept` の接頭辞が付いているヘッダは、クライアントが受け入れ可能なものに関する情報を示す。
(※3) `From`, `Host`, `Referer`, `User-Agent` は要求を開始したクライアントの詳細を特定するための情報を示す。
(※4) `If-` の接頭辞が付いているヘッダは、条件付きの要求を行いたいときに利用され、サーバは `If` の条件にマッチするときのみリソースを返す。ただし、サーバが `304 Not Modified` を返却した場合には、その条件は `Timestamp` か `ETag` を元に判断されるようになる。

#### 応答の形式/Response Format

応答の形式は要求のものと似ているが、ステータスラインとヘッダが異なる。ステータスラインは以下のようなもの。

```
{HTTP-Version}<SP>{Status-Code}<SP>{Reason-Phrase}<CRLF>
```

- `{HTTP-Version}` => 要求で送られたもの、この例では `HTTP/1.1`
- `<SP>` => 半角スペース
- `{Status-Code}` => 以前に述べたもののうちの一つ
- `{Reason-Phrase}` => ステータスコードを人が解釈しやすく表現した文章
    - 要求が成功した場合の典型的なものは `OK`

例えば以下のようになる。

```
HTTP/1.1 200 OK
```

レスポンスヘッダも (リクエストヘッダと同じように) 既定で利用するものは限られている。

ヘッダ名称|説明
:--|:--
Accept-Ranges|(言及なし)
Age|サーバでメッセージが生成されてからの経過時間 (単位: 秒)
ETag|エンティティの md5 ハッシュ、更新確認用
Location|リダイレクト時の新規 URL
Proxy-Authenticate|(言及なし)
Retry-After|(言及なし)
Server|メッセージを生成したサーバの特定用
Vary|(言及なし)
WWW-Authenticate|(言及なし)

### HTTP トラフィックを見るためのツール/Tools to View HTTP Traffic

HTTP 通信をモニタリングするためのツールは多い。いくつかの有名なものを上げてみよう。
`Chrome/Webkit inspector` は Web 開発者の間で愛用されている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95030/b4ed82a2-777e-a4bb-3403-53304374a7d4.png)

デバッグ用プロキシとしては、Fiddler や Charles も良い。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95030/2758ba89-ec1f-ff9e-f2bc-b0de14bd1aad.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95030/5df55e6c-8c77-6977-a67c-3eb5035fcc84.png)

また、コマンドライン用ツールであれば、`curl` や `tcpdump`, `tshark` といったものも HTTP トラフィックの観測に用いることができる。

### Web フレームワークやライブラリでの HTTP 利用/Using HTTP in Web Frameworks and Libraries

これまで要求・応答メッセージを見てきたところで、ライブラリやフレームワークにおける API がどのようにその仕組みを実現しているのかを見ていこう。ExpressJS for Node, Ruby on Rails そして jQuery Ajax をサンプルとして使う。

(以下は、各言語でどのように HTTP 通信のリクエスト・レスポンスを処理するのかという具体的なコードであったため割愛。)

## Summary

HTTP プロトコルについての弾丸ツアーはここまで。
URL 構造とリクエストメソッド、ステータスコード、これらが HTTP 通信の 3 本柱となる。
要求・応答メッセージはほぼ同一だが、最初の行とヘッダの内容が異なる。
最後には (訳注: 訳しませんでしたが) それぞれのフレームワークやライブラリでどのようにすればヘッダを変更できるのかも確認した。

HTTP の理解は、2 つのエンドポイントの間に完結かつシンプル、RESTful なインターフェースを提供するために必要不可欠。より大きなスケールにおいては、ネットワークの設計や、エンドユーザにより優れた体験を提供する一助ともなる。

## Conclusion

HTTP についてはここに記載されていることは大まかには把握していたものの、このように体系立てて読み返す機会がなかったので、読み返しついでに意訳を入れてみました。
素晴らしい記事をネットに上げてくださった著者の方と、本記事と出会うきっかけになった友人たちに感謝を。

[^1]: この記事は 2013 年に書かれたもののため。参照している RFC は古いものになっています。RFC 2616 は 2014 年に 7230-7237 へ置き換えられています。
[^2]: 一貫して元記事では verb と記載されているのですが `GET` や `POST` などリクエストメソッドの話のため、基本的にはメソッドと訳しています。
[^3]: 通信の目的である、実際に転送されるデータ内容との関係はない
[^4]: 要求対象のリソースやクライアントの情報が入る
[^5]: 応答に関する情報やサーバの情報が入る
[^6]: コンテンツの長さや種別など