[1] [CODE(HTTP)@en[[[Set-Cookie:]]]] 欄には名前と値を [CODE(HTTP)[[[=]]]]
で連結した組を [CODE(HTTP)[[[;]]]] で区切って複数記述することができます。これを
[[Cookie]]の[DFN[[RUBYB[[[属性]]]@en[attribute]]]]といいます。

[3] 元々の [[Netscape]] の [[Cookie]] 仕様 (>>2) では最初の任意の名前と値の組 ([[cookie-pair]])
も[[属性]]と呼んでいましたが、 [[RFC 6265]] はその後に続く [[cookie-av]] だけを[[属性]]と呼んでいます。

* 仕様書

[REFS[
- [377] '''[CITE@en[RFC 6265 - HTTP State Management Mechanism]] ([TIME[2012-03-01 09:57:02 +09:00]] 版) <http://tools.ietf.org/html/rfc6265#section-4.1>'''
- [24] [CITE[RFC Errata Report]] ([TIME[2014-11-04 07:53:14 +09:00]] 版) <http://www.rfc-editor.org/errata_search.php?rfc=6265>
]REFS]

* 構文

[378] [CODE(HTTP)@en[[[Set-Cookie:]]]] 欄の構文は次のように定義されています。
[[起源鯖]]はこの構文に従う[['''べきです''']] [SRC[>>377]]。

;; [379] なぜか違反を完全に禁止はされていません...

[FIG[
[FIGCAPTION[
[380] [[RFC 6265]] [SRC[>>377, >>24]] による [CODE(HTTP)@en[[[Set-Cookie:]]]] 欄の構文
]FIGCAPTION]

[PRE(ABNF code)[
 set-cookie-header = "Set-Cookie:" SP set-cookie-string
 set-cookie-string = cookie-pair *( ";" SP cookie-av )
 cookie-pair       = cookie-name "=" cookie-value
 cookie-name       = token
 cookie-value      = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
 cookie-octet      = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
                       ; US-ASCII characters excluding CTLs,
                       ; whitespace DQUOTE, comma, semicolon,
                       ; and backslash
 token             = <token, defined in [RFC2616], Section 2.2>

 cookie-av         = expires-av / max-age-av / domain-av /
                     path-av / secure-av / httponly-av /
                     extension-av
 extension-av      = *<any CHAR except CTLs or ";">
]PRE]
]FIG]

** 順序

[5] 名前と値の組の順序については明確に規定されていませんが、 [[Cookie]]
として設定する名前と値の組 [CODE(HTTP)[[VAR[NAME]]=[VAR[VALUE]]]] 
の後に他の[[属性]]を適当な順番で記述します。

;; [325] [[RFC 2109]], [[RFC 2965]] では [CODE(HTTP)[[VAR[NAME]]=[VAR[VALUE]]]] 
の後に他の[[属性]]が来ること、他の[[属性]]の順序は任意であることが明記されていました。
[SRC[>>324, >>323]]

[326] [[属性]]の名前が同じものが複数回使われている場合の解釈については触れられていません。

;; [327] [[RFC 2109]] ではその場合の動作は[[未定義]]とされていました [SRC[>>324]]。
[[RFC 2965]] では最初に現れたものが有効であるとされていました [SRC[>>323]]。

** 大文字と小文字

[12] [[属性]]の名前や値の[[大文字と小文字を区別しない]]かどうかは仕様上明記されていません。
仕様書上は[[属性]]の名前はすべて[[小文字]]で記述されています。

;; [314] [[RFC 2109]], [[RFC 2965]] では[[名前]]の[[大文字と小文字を区別しない]]とされていました。
[SRC[>>313, >>310]]

** 重複

[385] 同じ名前の[[属性]]は複数ある[['''べきではありません''']]。 [SRC[>>377]]

;; [386] なぜか完全に禁止はされていません。

[18] 複数指定された場合、最後の[[属性]]が採用されます。

;; [CODE(HTTP)@en[[[Set-Cookie:]]]] 参照。

** 引用文字列

[315] [[HTTP]] や [[MIME]] の他の[[頭欄]]では値は[RUBYB[[[字句]]]@en[[[token]]]]か[RUBYB[[[引用文字列]]]@en[[[quoted-string]]]]のいずれかとするのが一般的ですが、
[[Netscape Cookie]] では[[引用文字列]]は認められて''おらず''、
どんな[[文字列]]であってもそのまま値としなければなりません。

;; [316] [[RFC 2109]], [[RFC 2965]] では値は[[字句]]または[[引用文字列]]とされていました。
[SRC[>>313, >>310]]

[19] 仮に[[引用文字列]]を指定しても、 [CODE["]] は区切りではなく値の一部として認識されますし、
[CODE["]] で括ったとしてもその中の [CODE(HTTP)[[[;]]]] や [CODE(HTTP)[[[=]]]]
は区切りとして認識されてしまいます。

** 空白

[317] [[HTTP]] や [[MIME]] の他の[[頭欄]]では特定の[[字句]]の間には[RUBYB[[[空白]]]@en[white space]]を挿入することが認められていましたが、
[[Netscape Cookie]] で認められているのかどうかは不明確です。仕様書の記述から、
「[CODE(HTTP)[[[;]]]]」の直後に挿入することは認められているようです。

;; [316] [[RFC 2109]], [[RFC 2965]] では[[字句]]の間に[[空白]]を入れてもよいとされていました。
[SRC[>>313, >>310]]

[20] [[RFC 6265]] では [CODE(HTTP)[[[;]]]] の直後に1つだけ [CODE(charname)@en[[[SPACE]]]]
を書かなければならないと明確化されました。

* 構文解析

[16] [[ヘッダー]]全体の[[構文解析]]については、 [CODE(HTTP)@en[[[Set-Cookie:]]]]
や [CODE(HTTP)@en[[[Set-Cookie:]]]] を参照してください。

[17] 各[[属性]]の値の[[構文解析]]については、各[[属性]]の項を参照してください。

* 属性の一覧

[6] 
,*[[属性名]],*説明,*状態,*出典
,[VAR[NAME]],[CODE[cookie-pair]] >>7,"[[事実上の標準]], [[IETF]] [[提案標準]]","[[Netscape Cookie]], [DEL[[[RFC 2109]]]], [[RFC 2965]]"
,[CODE(HTTP)@en[[[Comment]]]],[[注釈]],[[IETF]] [[提案標準]],"[DEL[[[RFC 2109]]]], [[RFC 2965]]"
,[CODE(HTTP)@en[[[CommentURL]]]] ※,[[注釈]]の [[URL]],[[IETF]] [[提案標準]],"[[RFC 2965]]"
,[CODE(HTTP)@en[[[Domain]]]],対象となる[[ドメイン]],"[[事実上の標準]], [[IETF]] [[提案標準]]","[[Netscape Cookie]], [DEL[[[RFC 2109]]]], [[RFC 2965]]"
,[CODE(HTTP)@en[[[Expires]]]],有効期限,[[事実上の標準]],[[Netscape Cookie]]
,[CODE(HTTP)@en[[[HttpOnly]]]],[[HTTP]] 専用,[[事実上の標準]],
,[CODE(HTTP)@en[[[Max-Age]]]],[[寿命]],"[[IETF]] [[提案標準]], [[事実上の標準]]","[DEL[[[RFC 2109]]]], [[RFC 2965]]"
,[CODE(HTTP)@en[[[Path]]]],対象となる [[path]],"[[事実上の標準]], [[IETF]] [[提案標準]]","[[Netscape Cookie]], [DEL[[[RFC 2109]]]], [[RFC 2965]]"
,[CODE(HTTP)@en[[[Port]]]] ※,対象となる[[ポート]],[[IETF]] [[提案標準]],"[[RFC 2965]]"
,[CODE(HTTP)@en[[[Secure]]]],[[安全]]でなければならないか否か,"[[事実上の標準]], [[IETF]] [[提案標準]]","[[Netscape Cookie]], [DEL[[[RFC 2109]]]], [[RFC 2965]]"
,[CODE(HTTP)@en[[[Version]]]],[[Cookie]] 仕様の[[版]],[[IETF]] [[提案標準]],"[DEL[[[RFC 2109]]]], [[RFC 2965]]"

- [CODE(HTTP)@en[[[$Domain]]]]
- [CODE(HTTP)@en[[[$Port]]]]
- [CODE(HTTP)@en[[[$Path]]]]
- [CODE(HTTP)@en[[[$Version]]]]
- [CODE[SameSite]]
- [CODE[Origin][Origin-Cookie]]

[329] 「※」は [CODE(HTTP)@en[[[Set-Cookie2:]]]] [[頭欄]]でのみ定義されていることを表します。
また、 [CODE(HTTP)@en[[[Expires]]]] は [CODE(HTTP)@en[[[Set-Cookie:]]]] [[頭欄]]でのみ定義されています。

* [CODE(HTTP)[cookie-pair]] ([CODE(HTTP)@en[[VAR[NAME]]=[VAR[VALUE]]]])

[7] [CODE(HTTP)@en[[[Set-Cookie:]]]] 欄にはまず最初に設定する名前と値の組を[[属性]]として指定します。
この部分は [DFN[[CODE[cookie-pair]]]] と呼ばれています。

[8] この[[属性]]は必須です。 [SRC[>>2, >>380]]

** 名前

[4] [[RFC 6265]] の定義では、名前は [[RFC 2616]] の [CODE(ABNF)[[[token]]]] とされています。
すなわち、 [[ASCII]] [[印字可能文字]]を使うことができますが、一部の[[記号]]は使うことができません。
[[起源鯖]]はそれに合致しない値を使う[['''べきではありません''']]。 [SRC[>>380]]

;; [14] [[Netscape]] の定義では [CODE(char)[[[;]]]], [CODE(char)[[[,]]]], 
[RUBYB[[[空白]]]@en[white space]] を除く任意の値とされており [SRC[>>2]]、それよりずっと厳しくなっています。

[21] [[利用者エージェント]]は不適合な値も扱える必要があるため、
名前は ([CODE[[[=]]]] と [CODE[[[;]]]] と[[空白]]以外の)
1[[バイト]]以上の[[バイト列]]として扱うことになります。

*** 予約名

[327] 他の[[属性]]と同じ [VAR[NAME]] を使っていいのかどうかは不明です。

[387] [[RFC 6265]] では [[cookie-pair]] と[[属性]]は定義上別のものとされており、
特に[[属性]]の名前との衝突は禁止されていないものと思われます。

;; [328] [[RFC 2109]] でも明記されていませんでした。
[[RFC 2965]] では[[属性]]の名前と同じ [VAR[NAME]] を使って[['''構いません''']]。
[SRC[>>323]]

;; [325] [[RFC 2109]], [[RFC 2965]] では [CODE(char)[[[$]]]]
から始まる名前は[[予約]]となっており、
[RUBYB[[[応用]]]@en[application]]が使っては[['''ならない''']]とされていました。
[SRC[>>324, >>323]] ([CODE(HTTP)@en[[[Cookie:]]]] [[頭欄]]で [CODE(char)[[[$]]]]
は特別な[[属性]]を表すために使われていました。)

** 値

[15] [[RFC 6265]] の定義では、値には [CODE(char)[[[;]]]], [CODE(char)[[[,]]]], 
[CODE(char)[[["]]]], [CODE(char)[[[\]]]], [[空白]]を含まない [[ASCII]]
[[印字可能文字]]を0文字以上続けた列か、またはそれを [CODE(char)[[["]]]] で括ったものを使うことになっています。
[SRC[>>380]]

;; [9] [[Netscape]] の元々の定義では、値は任意の値ですが、 [CODE(char)[[[;]]]], [CODE(char)[[[,]]]], 
[RUBYB[[[空白]]]@en[white space]]は使えないとされていました。 [SRC[>>2]]

;; [381] それまで使えていた [CODE(char)[[["]]]] と [CODE(char)[[[\]]]] が [[RFC 6265]]
では禁止されていて、かわりに [CODE(char)[[["]]]] で括る構文が規定されています。
[[利用者エージェント]]は単に[[起源鯖]]から受け取った文字列をそのまま[[鯖]]に送り返すだけなので、
形式的に新しい構文を追加したところで互換性には影響が無いのでしょうが、
今更あえて別構文として定義する意味があるのかは疑問です。

[382] 値の意味は [[RFC 6265]] では定義しないとされています。 [SRC[>>377]]

[383] 任意のデータを扱いたい時は、[[利用者エージェント]]との互換性を最大化するため、
何らかの方法で値を[[符号化]]しておくことが[[推奨]]されています。 [SRC[>>377]]

[10] その[[符号化]]の方法は具体的には規定されていませんが、 [[RFC 6265]] では [[Base64]]
を例示しており [SRC[>>377]]、 [[Netscape]] の仕様では[[パーセント符号化]]を[RUBYB[推奨]@en[recommended]]
[SRC[>>2]] していました。

;; [384] このため [[CGI]] などで [[Cookie]] を扱う[[ライブラリー]]の類では [[Cookie]]
の値を[[パーセント符号化]]、[[パーセント復号]]する実装になっていることもよくあります。

;; [11] [[Netscape]] の仕様上は >>9 の記号類以外の任意の[[文字]]が使えることになっていますが、その「[[文字]]」
全体の[[集合]]が何かは不明です。[[HTTP]] 
[[頭部]]で[[非ASCII文字]]を使う方法は標準不在な状況で、[CODE(HTTP)@en[[[Set-Cookie:]]]]
欄に関しても何ら [WEAK[(利用できるのかどうかも)]] 定められていません。
[[文書]]の[[文字コード]]や[[利用者エージェント]]の種類によって違った解釈をされる可能性があります。
現実の[[CGIスクリプト]]等の実装は特に気にしていなかったり、
[[百分率符号化]]を用いていたりです。[[百分率符号化]]を使うのは仕様書の >>10
の記述の影響でしょうか。

;; [13] [CODE(HTTP)@en[[[=]]]] も名前か値かその両方かで使えないはずなのですが、
[[Netscape]] の仕様上は明記されていませんでした。 [[RFC 6265]] によれば名前の方で使えなく、
値には使うことができます。

;; [348] [[RFC 2109]] や [[RFC 2965]] では名前は [CODE(ABNF)@en[[[token]]]]、
値は [CODE(ABNF)@en[[[token]]]] か [CODE(ABNF)@en[[[quoted-string]]]] とされていました。
また、 [[RFC 2965]] の [CODE(HTTP)@en[[[Comment]]]] [[属性]]では値は [[UTF-8]]
とされていました。 [SRC[>>313, >>310]]

[22] [[利用者エージェント]]は不適合な値も扱える必要があるため、
値は ([CODE[[[=]]]] と [CODE[[[;]]]] と[[空白]]以外の)
0[[バイト]]以上の[[バイト列]]として扱うことになります。

** 重複の扱い

[388] 1つの [[HTTP]] [[応答]]に複数個の [CODE(HTTP)@en[[[Set-Cookie:]]]] 欄を含めることはできますが、
同じ名前の [[cookie-pair]] で複数個指定する[['''べきではない''']]とされています [SRC[>>377]]。

;; [389] [[Netscape]] の仕様では、その場合どれが採用されるのか明記されていませんでした。

[390] 複数の [[HTTP]] [[応答]]によって複数の [CODE(HTTP)@en[[[Set-Cookie:]]]] 
が同時に1つの[[利用者エージェント]]に対して送り返された場合、これは[[競合条件]]であり、
結果は予測できません。 [SRC[>>377]]

** サイズ制限

;; [23] [[Cookieのサイズ制限]]参照。

* 未知の属性

[27] [[鯖]]が [CODE(HTTP)@en[[[Set-Cookie:]]]] に未知の[[属性]]を指定することは構文上は認められています。
仕様上は明示的には禁止されていません。

[25] [[利用者エージェント]]は、未知の[[属性]]を無視しなければなりません。

;; [CODE(HTTP)@en[[[Set-Cookie:]]]] を参照。

;; [342] 未知の[[属性]]の扱いは [[RFC 2109]] では明確には規定されていませんでしたが、 [[RFC 2965]]
では[[利用者エージェント]]は未知の[[属性]]を無視し[['''なければなりません''']]とされていました
[SRC[>>341]]。

[26] 他の [[HTTPヘッダー]]の多くは現在では [[IANA登録簿]]が設けられていますが、
[CODE(HTTP)@en[[[Set-Cookie:]]]] に関しては [[IANA登録簿]]がありません。

[28] 歴史的には、 [[Netscape Cookie]] に含まれなかった [CODE(HTTP)@en[[[Max-Age]]]]
[[属性]]と [CODE(HTTP)@en[[[HttpOnly]]]] [[属性]]が新たに追加されています。
今後も更に[[属性]]が追加される可能性はあります。しかし大量に新たな[[属性]]が標準に追加されたり、
[[鯖]]や[[利用者エージェント]]が独自の[[属性]]を積極的に実装したりすることは当面なさそうです。

* 歴史

** Netscape Cookie

[REFS[
- [2] [[Netscape Cookie]]
-- [CITE[Client Side State - HTTP Cookies]] <http://web.archive.org/web/19961125090609/http://www2.netscape.com/newsref/std/cookie_spec.html>
]REFS]