[1] [[HTTP]] の [DFN[[CODE(HTTP)@en[[[Cookie:]]]] [[要求頭欄]]]]は、[[Cookie]] を[[鯖]]に送信するためのものです。 * 仕様書 [REFS[ - [61] [CITE@en[RFC 6265 - HTTP State Management Mechanism]] ([TIME[2012-03-01 09:57:02 +09:00]] 版) - [64] '''[CITE@en[RFC 6265 - HTTP State Management Mechanism]] ([TIME[2012-03-01 09:57:02 +09:00]] 版) ''' ]REFS] * 構文 [FIG[ [FIGCAPTION[ [65] [[RFC 6265]] による [[ABNF]] 構文 ]FIGCAPTION] [PRE(ABNF code)[ cookie-header = "Cookie:" [[OWS]] cookie-string [[OWS]] cookie-string = [[cookie-pair]] *( ";" SP [[cookie-pair]] ) ]PRE] ]FIG] [66] [[鯖]]が [[RFC 6265]] に適合する [CODE(HTTP)@en[[[Set-Cookie:]]]] 欄を送信し、 [[利用者エージェント]]が [[RFC 6265]] に適合する処理を行った場合、 [[鯖]]は >>65 の構文に適合する [CODE(HTTP)@en[[[Cookie:]]]] 欄を受け取るはずです [SRC[>>64]]。 [67] ここで [[cookie-pair]] は[[鯖]]が送信し、[[利用者エージェント]]が蓄積していたものをそれぞれ表しています。 それぞれの名前と値の意味は [[RFC 6265]] では定義しておらず、当該[[アプリケーション]]における意味に従って処理されるべきものです。 [CODE(HTTP)@en[[[Set-Cookie:]]]] 時に指定した[[属性]]は [CODE(HTTP)@en[[[Cookie:]]]] には含まれておらず、どの [CODE(HTTP)@en[[[Path]]]] に対して発行したものなのか、などを [CODE(HTTP)@en[[[Cookie:]]]] だけから判定することはできません。 [SRC[>>64]] [70] しかし実際には[[鯖]]も[[利用者エージェント]]も [[RFC 6265]] に適合するとは限りません。 [[利用者エージェント]]が [[RFC 6265]] に従って [CODE(HTTP)@en[[[Set-Cookie:]]]] 欄を構文解析していると仮定すると、 [[クッキー]]の名前や値は [CODE(HTTP)@en[[[token]]]] で認められない文字を含むことがあります。 先頭と末尾が[[空白]]ではなく、 [CODE(HTTP)[[[;]]]] と [CODE(HTTP)[[[=]]]] を含まない任意の1文字以上の文字列が名前となり得ます。また値は先頭と末尾が[[空白]]ではなく、 [CODE(HTTP)[[[;]]]] を含まない任意の0文字以上の文字列となります。 [71] [[HTTP]] では一般に複数の同じ名前の[[ヘッダー]]を [CODE(HTTP)[[[,]]]] で連結しても等価とされていますが、[[クッキー]]の名前や値に [CODE(HTTP)[[[,]]]] が含まれる可能性があり、これが成立しません。 ([[RFC 6265]] の構文は [CODE(HTTP)[[[,]]]] を名前や値に認めていないので、適合する[[鯖]]が発行した[[クッキー]]なら曖昧性はありませんが、 そうでないものを[[鯖]]側で処理する可能性を想定するのであれば、 [CODE(HTTP)[[[,]]]] で結合・分割できません。) ;; [72] [[RFC 6165]] は[[鯖]]がこの辺りをどう処理するべきか特に規定していません。 通常は[[鯖]]が名前や値を制御できるので、そのようなものを出力しなければ良いだけです。 ただ実際には他の[[鯖]]から第三者的に発行することもできてしまいますし、 [[鯖]]側の実装と [[JavaScript]] の実装を別の著者が行なってい (て [[HTTP]] の制限を見落としてい) る場合など、まったく無視できる問題でもありません。 * 処理モデル ;; [16] [[RFC 2109]] と [[RFC 2965]] は、[[利用者エージェント]]は各種規則に基づき [CODE(HTTP)@en[[[Cookie:]]]] [[応答]]頭欄を送信する[['''べきである''']]、としていました。 [SRC[>>14, >>15]] ** 利用者エージェントの処理 [4] [[HTTP]] [[要求]]を構築する際に、[[要求URL]] と[[利用者エージェント]]が保持している [[Cookie]] を比較し、[[一致]]するものであれば [CODE(HTTP)@en[[[Cookie:]]]] [[要求頭欄]]に入れます。 [CODE(HTTP)@en[[[Cookie:]]]] [[欄]]には [[Cookie]] の名前と値の組を [PRE(HTTP example code)[ Cookie: NAME1=OPAQUE_STRING1; NAME2=OPAQUE_STRING2 ... ]PRE] のように並べて記述します。 [SRC[>>2]] [7] 同じ名前の値は、[CODE(HTTP)@en[[[Domain]]]] や [CODE(HTTP)@en[[[Path]]]] が同じなら設定時に古いものが上書きされますが、 違っていれば両方共残ります。その場合には [CODE(HTTP)@en[[[Cookie:]]]] [[頭欄]]にも両方共含められます。 [SRC[>>2]] *** 順序 [9] より具体的な [CODE(HTTP)@en[[[Path]]]] の [[Cookie]] はより一般的な [CODE(HTTP)@en[[[Path]]]] の [[Cookie]] よりも先にくるべきです。例えば [CODE(HTTP)@en[a=1; path=/foo]] と [CODE(HTTP)@en[b=2; path=/foo/bar]] があれば、 [CODE(HTTP)@en[b=2; a=1]] のように送るべきです。 [SRC[>>2]] [8] 同じ [CODE(HTTP)@en[[[Path]]]] の時の名前と値の組の順序は明確に規定されていません。 *** リダイレクト [11] [[リダイレクト]]によって再度 [[HTTP]] [[要求]]を送信する場合であっても、 (新しい [[URL]] に関して) 通常通り適用される [[Cookie]] を調べて [CODE(HTTP)@en[[[Cookie:]]]] [[頭欄]]を構築するべきです。 ** 串の処理 [62] [CODE(HTTP)@en[[[Cookie:]]]] 欄の有無は[[キャッシュ可能性]]に影響しません [SRC[>>61]]。 [10] [[串]]は [CODE(HTTP)@en[[[Cookie:]]]] をそのまま[[鯖]]へと転送するべきです。 それがたとえ [CODE(HTTP)@en[[[If-Modified-Since:]]]] つきの[[条件付き要求]]であってもです。 [SRC[>>2]] * 鯖の処理モデル [63] [[起源鯖]]は [CODE(HTTP)@en[[[Cookie:]]]] 欄やその中身を無視して構いません。 [SRC[>>61]] ** 順序 [68] [[クッキー]]は順番に並べることになってはいますが、[[鯖]]ではそれに依存する[['''べきではありません''']]。 特に、同じ名前の[[クッキー]]が複数ある場合に、これがどの順番で現れるかに依存する[['''べきではありません''']]。 [SRC[>>64]] [69] [CODE[CGI::Cookie]] ([TIME[2011-12-30 23:35:34 +09:00]] 版) は、古い [[Netscape]] が複数の同じ名前の[[クッキー]]を送ることがあり、一番前のものが最新であるとしてそれを使っています。 * 複数の頭欄 [5] [CODE(HTTP)@en[[[Cookie:]]]] [[要求頭欄]]が複数あっても良いのかどうかは仕様上明確になっていません。 * 歴史 ** Netscape Cookie [REFS[ - [2] [[Netscape Cookie]]: [CITE[Client Side State - HTTP Cookies]] ]REFS] [FIG[ [3] > When requesting a URL from an HTTP server, the browser will match the URL against all cookies and if any of them match, a line containing the name/value pairs of all matching cookies will be included in the HTTP request. Here is the format of that line: > Cookie: NAME1=OPAQUE_STRING1; NAME2=OPAQUE_STRING2 ... ;; [SRC[>>2]] ]FIG] ** RFC の [CODE(HTTP)@en[Cookie:]] 頭欄の定義 [19] [[RFC 2109]], [[RFC 2965]] では [[Netscape Cookie]] とはあまり互換性がない [CODE(HTTP)@en[[[Cookie:]]]] [[頭欄]]が定義されていました [SRC[>>17, >>18]]。 概要: - [20] 名前と値の組が1つ以上含まれるのは [[Netscape Cookie]] と同じでした。 - [35] 名前は [CODE(HTTP)@en[[[token]]]]、値は [CODE(HTTP)@en[[[token]]]] または [CODE(HTTP)@en[[[quoted-string]]]] でなければなりません [SRC[>>17, >>18]]。 - [21] 名前と値の組の前に [[Cookie]] の[[版]]を [CODE(HTTP)@en[[[$Version]]=1]] のように指定することになっていました [SRC[>>17, >>18]]。 -- [22] [DFN[[CODE(HTTP)@en[$Version]]]] には送信する [[Cookie]] に対応する [CODE(HTTP)@en[[[Set-Cookie]]]]、[CODE(HTTP)@en[[[Set-Cookie2]]]] の [CODE(HTTP)@en[[[Version]]]] と同じものを指定することになっていました。それがない時は [CODE(HTTP)[[[0]]]] を使うことになっていました [SRC[>>17, >>18]]。 -- [24] 混在する場合どうするのでしょうか。 -- [33] [CODE(HTTP)@en[[[Set-Cookie]]]], [CODE(HTTP)@en[[[Set-Cookie2]]]] のどちらも [CODE(HTTP)@en[[[Version]]]] は 1 で区別できませんが、それでいいのでしょうか。。。 -- [34] [CODE(HTTP)@en[[[Set-Cookie2]]]] は [CODE(HTTP)@en[[[Version]]]] が必須のはずですが、ない時の規定が残っているのは消し忘れでしょうか。。。 - [25] 名前と値の組の後に [CODE(HTTP)@en[; [[$Path]]=...]] のように[[属性]]を指定することになっていました [SRC[>>17, >>18]]。 -- [26] [DFN[[CODE(HTTP)@en[[[$Path]]]]]] [SRC[>>17, >>18]], [DFN[[CODE(HTTP)@en[[[$Domain]]]]]] [SRC[>>17, >>18]], [DFN[[CODE(HTTP)[[[$Port]]]]]] [SRC[>>18]] がありました。 -- [27] [CODE(HTTP)@en[[[Set-Cookie]]]], [CODE(HTTP)@en[[[Set-Cookie2]]]] に [CODE(HTTP)@en[[[Path]]]], [CODE(HTTP)@en[[[Domain]]]], [CODE(HTTP)@en[[[Port]]]] 各[[属性]]があった場合は同じ値 (または同じように値なし) でなければ[['''なりません''']]でした [SRC[>>17, >>18]]。[[属性]]自体がなかった場合には [CODE(HTTP)@en[[[$Path]]]], [CODE(HTTP)@en[[[$Domain]]]] は省略する[['''べき''']] [SRC[>>17, >>18]]、 [CODE(HTTP)@en[[[$Port]]]] は省略しなければ[['''ならない''']] [SRC[>>18]] とされていました。 -- [28] つまり、[[属性]]がなくて既定値が補われた場合と明示的に値が指定されていた場合は区別して蓄積しておく必要があるということですね。 - [29] 名前と値の組の順序は、[CODE(HTTP)@en[[[Path]]]] がより具体的なものほど前に来る [SRC[>>17, >>18]] とされていました。 -- [30] [[Netscape Cookie]] と同じですね。 -- [31] それ以外の [CODE(HTTP)@en[[[Domain]]]] 等と順序の関係は[RUBYB[規定しない]@en[unspecified]] [SRC[>>17, >>18]] とされていました。 - [32] 名前と値の組同士の区切りは[[後方互換性]]のため [CODE(HTTP)[[[;]]]] とされていました。 一方で将来との互換性のため[[鯖]]は [CODE(HTTP)[[[,]]]] も受け付けなければ[['''ならない''']]とされていて、 [[ABNF]] 構文上もそうなっていました。 [SRC[>>17, >>18]] - [38] [[鯖]]は、 [CODE(char)[[[$]]]] からはじまる名前を特別な意味を持つものとして解釈する[['''べきです''']] [SRC[>>36, >>37]] とされていました。 -- [39] なぜか MUST ではなくて SHOULD でした。 - [40] [CODE(char)[[[$]]]] 付きの[[属性]]は、前から順に調べていって、一番最初の [CODE(char)[[[$]]]] 無しの[[属性]]の手前までは全体に適用される[[属性]]、その後は直前の [CODE(char)[[[$]]]] 無しの[[属性]]に適用される[[属性]]と解釈するとされていました [SRC[>>36]]。 -- [41] この規定は [[RFC 2109]] にはありましたが、なぜか [[RFC 2965]] にはありませんでした。 - [44] [[串]]は自身で勝手に [CODE(HTTP)@en[[[Cookie:]]]] を[[応答]]に付け足しては[['''なりません''']] [SRC[>>42, >>43]]。 [46] [[Netscape Cookie]] の互換性について、次のようなことが言われていました [SRC[>>45]]。 - [47] 旧クライアントは新しい Cookie を受け取ると知らないものは無視し、理解できるものを使って [CODE(HTTP)@en[[[Cookie:]]]] を送る - [48] 新クライアントは Cookie を常に新形式で送る - [49] 旧鯖は「[CODE(HTTP)[[[$]]]」で始まる[[属性]]をそういう [[Cookie]] だと思って無視する -- [50] ただし「[CODE(HTTP)[[[,]]]]」区切りではなく「[CODE(HTTP)[[[;]]]]」区切りだと思っている - [51] 新鯖は「[CODE(HTTP)@en[[[$Version]]]]」の有無で新旧クライアントを区別できる [52] >>46 を読むと一見[[後方互換性]]が保たれていると錯覚してしまいがちですが、 新 Cookie では[[属性]]の値が[[引用文字列]]でもあり得るので、 旧クライアント・旧鯖は引用符も値の一部だと思ってしまうという問題があります。 - [54] [[起源鯖]]はクライアントが新 Cookie を理解すると分かっていないときは、引用符が必要な値に注意するべきです。 [SRC[>>53]] - [55] 新しいクライアントは [[Cookie]] の版がすべて [[RFC 2109]] 以降であるときだけ値を引用符で括るべきです。 [SRC[>>53]] [56] >>48 より新クライアントは常に新 Cookie を送ることになっているので、 常に[[引用文字列]]で送る新クライアントは旧鯖とは正しくやり取りできなくなり、 >>55 のような注意が必要になるわけです。でも >>54 のように注意しろと言われても困った話です。 引用符が必要な値だけど引用符を使えないときは引用符を外していいのでしょうか。 でも外すと [[RFC 2109]] には適合しなくなります。 [57] また、 [[RFC 2109]] は「[CODE(HTTP)[[[=]]]]」の周りの[[空白]]についても、 [[Netscape Cookie]] では認められていなかったので注意するよう書いています [SRC[>>53]]。 *** 仕様書 - [12] [DEL[[[RFC 2109]]]] ([[廃止]]済み) -- [14] [CSECTION@en[4.2.1 General]] -- [17] '''[CSECTION@en[4.3.4 Sending Cookies to the Origin Server]]''' -- [36] [CSECTION@en[4.4 How an Origin Server Interprets the Cookie Header]] -- [42] [CSECTION@en[4.5 Caching Proxy Role]] -- [45] [CSECTION@en[10.1.1 Extended Cookie Header]] -- [53] [CSECTION@en[10.1.3 Punctuation]] - [13] [[RFC 2965]] -- [15] [CSECTION@en[3.2.1 General]] -- [18] '''[CSECTION@en[3.3.4 Sending Cookies to the Origin Server]]''' -- [37] [CSECTION@en[3.4 How an Origin Server Interprets the Cookie Header]] -- [43] [CSECTION@en[3.5 Caching Proxy Role]] * 実装 [58] [CITE[Mojo::Cookie::Request - search.cpan.org]] ([TIME[2010-08-01 16:26:16 +09:00]] 版) [[Netscape Cookie]] に加え、 [[RFC 2109]] か [[RFC 2965]] の [CODE(HTTP)@en[[[Cookie:]]]] の構文解析に対応しています。 * CGI メタ変数 [59] [[CGIメタ変数]] [DFN[[CODE(HTTP)@en[[[HTTP_COOKIE]]]]]] は [[HTTP]] の [CODE(HTTP)@en[[[Cookie:]]]] 欄に対応します。詳しくは [CODE(CGI)[[[HTTP_*]]]] を参照してください。 [60] [[WebSite]] は [CODE(CGI)@en[[[HTTP_COOKIE]]]] のかわりに[[メタ変数]] [DFN[[CODE(CGI)@en[[[COOKIE]]]]]] を使っているそうです。 * 関連 [6] [[Cookie]] を設定する [CODE(HTTP)@en[[[Set-Cookie:]]]] [[頭欄]]と同じように[[名前]]と値の組を並べる形式ではありますが、 [CODE(HTTP)@en[[[Set-Cookie:]]]] は1つの名前と値の組を設定するのに対し、 [CODE(HTTP)@en[[[Cookie:]]]] は複数の名前と値の組を1つにまとめて記述できます。