Cookie Store API Standard
Cookie Store API
Living Standard — Last Updated
20 April 2026
Participate:
GitHub whatwg/cookiestore
new issue
open issues
Chat on Matrix
Commits:
GitHub whatwg/cookiestore/commits
Snapshot as of this commit
@cookiestoreapi
Tests:
web-platform-tests cookiestore/
ongoing work
Translations
(non-normative)
简体中文
한국어
Abstract
An asynchronous JavaScript cookies API for documents and service workers.
1.
Introduction
This section is non-normative.
This standard defines an asynchronous cookie API for scripts running in HTML documents and
service workers
HTTP cookies
have, since their origins at Netscape
(documentation preserved by archive.org)
, provided a
valuable state-management mechanism
for the web.
The synchronous single-threaded script-level
document.cookie
interface to cookies has been a source of
complexity and performance woes
further exacerbated by the move in many browsers from:
a single browser process,
a single-threaded event loop model, and
no general expectation of responsiveness for scripted event handling while processing cookie operations
… to the modern web which strives for smoothly responsive high performance:
in multiple browser processes,
with a multithreaded, multiple-event loop model, and
with an expectation of responsiveness on human-reflex time scales.
On the modern web a cookie operation in one part of a web application cannot block:
the rest of the web application,
the rest of the web origin, or
the browser as a whole.
Newer parts of the web built in service workers
need access to cookies too
but cannot use the synchronous, blocking
document.cookie
interface at all as they both have no document and also cannot block the event loop as that would interfere with handling of unrelated events.
1.1.
Alternative to
document.cookie
Today writing a cookie means blocking your event loop while waiting for the browser to synchronously update the cookie jar with a carefully-crafted cookie string in
Set-Cookie
format:
document
'__Secure-COOKIENAME=cookie-value'
'; Path=/'
'; expires=Fri, 12 Aug 2016 23:05:17 GMT'
'; Secure'
'; Domain=example.org'
// now we could assume the write succeeded, but since
// failure is silent it is difficult to tell, so we
// read to see whether the write succeeded
var
successRegExp
/(^|; ?)__Secure-COOKIENAME=cookie-value(;|$)/
if
String
document
).
match
successRegExp
))
console
log
'It worked!'
);
else
console
error
'It did not work, and we do not know why'
);
What if you could instead write:
const
one_day_ms
24
60
60
1000
cookieStore
set
name
'__Secure-COOKIENAME'
value
'cookie-value'
expires
Date
now
()
one_day_ms
domain
'example.org'
}).
then
function
()
console
log
'It worked!'
);
},
function
reason
console
error
'It did not work, and this is why:'
reason
);
});
// Meanwhile we can do other things while waiting for
// the cookie store to process the write...
This also has the advantage of not relying on document and not blocking, which together make it usable from
service workers
, which otherwise do not have cookie access from script.
This standard also includes a power-efficient monitoring API to replace
setTimeout
-based polling cookie monitors with cookie change observers.
1.2.
Summary
In short, this API offers the following functionality:
write
(or "set") and delete (or "expire") cookies
read
(or "get")
script-visible
... including for specified in-scope request paths in
service worker
contexts
monitor
script-visible
cookies for changes using
CookieChangeEvent
... in long-running script contexts (e.g.
document
... for script-supplied in-scope request paths in
service worker
contexts
1.3.
Querying cookies
Both
documents
and
service workers
access the same query API, via the
cookieStore
property on the
global object
The
get()
and
getAll()
methods on
CookieStore
are used to query cookies.
Both methods return
Promise
s.
Both methods take the same arguments, which can be either:
a name, or
a dictionary of options (optional for
getAll()
The
get()
method is essentially a form of
getAll()
that only returns the first result.
Reading a cookie:
try
const
await
cookieStore
get
'session_id'
);
if
console
log
`Found
${
name
cookie:
${
value
);
else
console
log
'Cookie not found'
);
catch
console
error
`Cookie store error:
${
);
Reading multiple cookies:
try
const
await
cookieStore
getAll
'session_id'
});
for
const
of
console
log
`Result:
${
name
${
value
);
catch
console
error
`Cookie store error:
${
);
Service workers
can obtain the list of cookies that would be sent by a
fetch
to
any URL under their
scope
Read the cookies for a specific URL (in a
service worker
):
await
cookieStore
getAll
({
url
'/admin'
});
Documents
can only obtain the cookies at their current URL. In other words,
the only valid
url
value in
Document
contexts is the document’s URL.
The objects returned by
get()
and
getAll()
contain all the relevant information in the cookie store, not just the
name
and the
value
as in the older
document.cookie
API.
Accessing all the cookie data:
await
cookieStore
get
'session_id'
);
console
log
`Cookie scope - Domain:
${
domain
Path:
${
path
);
if
expires
===
null
console
log
'Cookie expires at the end of the session'
);
else
console
log
`Cookie expires at:
${
expires
);
if
secure
console
log
'The cookie is restricted to secure origins'
);
1.4.
Modifying cookies
Both
documents
and
service workers
access the same modification API, via the
cookieStore
property on the
global object
Cookies are created or modified (written) using the
set()
method.
Write a cookie:
try
await
cookieStore
set
'opted_out'
'1'
);
catch
console
error
`Failed to set cookie:
${
);
The
set()
call above is shorthand for using an options dictionary, as follows:
await
cookieStore
set
({
name
'opted_out'
value
'1'
expires
null
// session cookie
// By default, domain is set to null which means the scope is locked at the current domain.
domain
null
path
'/'
});
Cookies are deleted (expired) using the
delete()
method.
Delete a cookie:
try
await
cookieStore
delete
'session_id'
);
catch
console
error
`Failed to delete cookie:
${
);
Under the hood, deleting a cookie is done by changing the cookie’s expiration date to the past, which still works.
Deleting a cookie by changing the expiry date:
try
const
one_day_ms
24
60
60
1000
await
cookieStore
set
({
name
'session_id'
value
'value will be ignored'
expires
Date
now
()
one_day_ms
});
catch
console
error
`Failed to delete cookie:
${
);
1.5.
Monitoring cookies
To avoid polling, it is possible to observe changes to cookies.
In
documents
change
events are fired for all relevant cookie changes.
Register for
change
events in documents:
cookieStore
addEventListener
'change'
event
=>
console
log
${
event
changed
length
changed cookies`
);
for
const
in
event
changed
console
log
`Cookie
${
name
changed to
${
value
);
console
log
${
event
deleted
length
deleted cookies`
);
for
const
in
event
deleted
console
log
`Cookie
${
name
deleted`
);
});
In
service workers
cookiechange
events are fired against the global scope, but an explicit subscription is required, associated with the service worker’s registration.
Register for
cookiechange
events in a service worker:
self
addEventListener
'activate'
event
=>
event
waitUntil
async
()
=>
// Snapshot current state of subscriptions.
const
subscriptions
await
self
registration
getSubscriptions
();
// Clear any existing subscriptions.
await
self
registration
unsubscribe
subscriptions
);
await
self
registration
([
name
'session_id'
// Get change events for cookies named session_id.
]);
});
});
self
addEventListener
'cookiechange'
event
=>
// The event has |changed| and |deleted| properties with
// the same semantics as the Document events.
console
log
${
event
changed
length
changed cookies`
);
console
log
${
event
deleted
length
deleted cookies`
);
});
Calls to
subscribe()
are cumulative, so that independently maintained
modules or libraries can set up their own subscriptions. As expected, a
service worker
’s
subscriptions are persisted for with the
service worker registration
Subscriptions can use the same options as
get()
and
getAll()
The complexity of fine-grained subscriptions is justified
by the cost of dispatching an irrelevant cookie change event to a
service worker
which is much higher than the cost of dispatching an equivalent event
to a
Window
. Specifically, dispatching an event to a
service worker
might
require waking up the worker, which has a significant impact on battery life.
The
getSubscriptions()
allows a
service worker
to introspect
the subscriptions that have been made.
Checking change subscriptions:
const
subscriptions
await
self
registration
getSubscriptions
();
for
const
sub
of
subscriptions
console
log
sub
name
sub
url
);
2.
Concepts
2.1.
is normatively defined for user agents by
Cookies § User Agent Requirements
Per
Cookies § Storage Model
, a
has the following fields:
name
value
domain
path
http-only-flag
To
normalize a cookie name or value
given a
string
input
remove all U+0009 TAB and U+0020 SPACE that are at the start or end of
input
A cookie is
script-visible
when it is in-scope and its
http-only-flag
is unset. This is more formally enforced in the processing model, which consults
Cookies § Retrieval Model
at appropriate points.
A cookie is also subject to certain size limits. Per
Cookies § Storage Model
The combined lengths of the name and value fields must not be greater than 4096
bytes
(the
maximum name/value pair size
).
The length of every field except the name and value fields must not be greater than 1024
bytes
(the
maximum attribute value size
).
attribute-values are stored as
byte sequences
, not strings.
2.2.
Cookie store
cookie store
is normatively defined for user agents by
Cookies § User Agent Requirements
When any of the following conditions occur for a
cookie store
, perform the steps to
process cookie changes
A newly-created
is inserted into the
cookie store
A user agent evicts expired
from the
cookie store
A user agent removes excess
from the
cookie store
2.3.
Extensions to Service Workers
[Service-Workers]
defines
service worker registration
, which this specification extends.
service worker registration
has an associated
cookie change subscription list
which is a
list
each member is a
cookie change subscription
. A
cookie change subscription
is
tuple
of
name
(a
string
or null) and
url
3.
The
CookieStore
interface
Exposed
=(
ServiceWorker
Window
),
SecureContext
interface
CookieStore
EventTarget
Promise
CookieListItem
?>
get
USVString
name
);
Promise
CookieListItem
?>
get
optional
CookieStoreGetOptions
options
= {});
Promise
CookieList
getAll
USVString
name
);
Promise
CookieList
getAll
optional
CookieStoreGetOptions
options
= {});
Promise
undefined
set
USVString
name
USVString
value
);
Promise
undefined
set
CookieInit
options
);
Promise
undefined
delete
USVString
name
);
Promise
undefined
delete
CookieStoreDeleteOptions
options
);

Exposed
Window
attribute
EventHandler
onchange
};
dictionary
CookieStoreGetOptions
USVString
name
USVString
url
};
enum
CookieSameSite
"strict"
"lax"
"none"
};
dictionary
CookieInit
required
USVString
name
required
USVString
value
DOMHighResTimeStamp
expires
null
USVString
domain
null
USVString
path
= "/";
CookieSameSite
sameSite
= "strict";
boolean
partitioned
false
long
long
maxAge
null
};
dictionary
CookieStoreDeleteOptions
required
USVString
name
USVString
domain
null
USVString
path
= "/";
boolean
partitioned
false
};
dictionary
CookieListItem
USVString
name
USVString
value
};
typedef
sequence
CookieListItem
CookieList
3.1.
The
get()
method
= await cookieStore .
get
name
= await cookieStore .
get
options
Returns a promise resolving to the first in-scope
script-visible
value
for a given cookie name (or other options).
In a service worker context this defaults to the path of the service worker’s
script url
In a document it defaults to the path of the current document and does not respect changes from
replaceState()
or
document.domain
The
get(
name
method steps are:
Let
settings
be
this
’s
relevant settings object
Let
origin
be
settings
’s
origin
If
origin
is an
opaque origin
, then return
a promise rejected with
a "
SecurityError
DOMException
Let
url
be
settings
’s
creation URL
Let
be
a new promise
Run the following steps
in parallel
Let
list
be the results of running
query cookies
with
url
and
name
If
list
is failure, then
reject
with a
TypeError
and abort these steps.
If
list
is empty
, then
resolve
with null.
Otherwise,
resolve
with the first item of
list
Return
The
get(
options
method steps are:
Let
settings
be
this
’s
relevant settings object
Let
origin
be
settings
’s
origin
If
origin
is an
opaque origin
, then return
a promise rejected with
a "
SecurityError
DOMException
Let
url
be
settings
’s
creation URL
If
options
is empty
, then return
a promise rejected with
TypeError
If
options
["
url
"]
exists
Let
parsed
be the result of
parsing
options
["
url
"] with
settings
’s
API base URL
If
parsed
is failure or
parsed
does not
equal
url
with
exclude fragments
set to true,
then return
a promise rejected with
TypeError
If
parsed
’s
origin
and
url
’s
origin
are not the
same origin
then return
a promise rejected with
TypeError
Set
url
to
parsed
Let
be
a new promise
Run the following steps
in parallel
Let
list
be the results of running
query cookies
with
url
and
options
["
name
"]
with default
null.
If
list
is failure, then
reject
with a
TypeError
and abort these steps.
If
list
is empty
, then
resolve
with null.
Otherwise,
resolve
with the first item of
list
Return
3.2.
The
getAll()
method
= await cookieStore .
getAll
name
= await cookieStore .
getAll
options
Returns a promise resolving to the all in-scope
script-visible
value for a given cookie name (or other options).
In a service worker context this defaults to the path of the service worker’s
script url
In a document it defaults to the path of the current document and does not respect changes from
replaceState()
or
document.domain
The
getAll(
name
method steps are:
Let
settings
be
this
’s
relevant settings object
Let
origin
be
settings
’s
origin
If
origin
is an
opaque origin
, then return
a promise rejected with
a "
SecurityError
DOMException
Let
url
be
settings
’s
creation URL
Let
be
a new promise
Run the following steps
in parallel
Let
list
be the results of running
query cookies
with
url
and
name
If
list
is failure, then
reject
with a
TypeError
Otherwise,
resolve
with
list
Return
The
getAll(
options
method steps are:
Let
settings
be
this
’s
relevant settings object
Let
origin
be
settings
’s
origin
If
origin
is an
opaque origin
, then return
a promise rejected with
a "
SecurityError
DOMException
Let
url
be
settings
’s
creation URL
If
options
["
url
"]
exists
Let
parsed
be the result of
parsing
options
["
url
"] with
settings
’s
API base URL
If
parsed
is failure or
parsed
does not
equal
url
with
exclude fragments
set to true,
then return
a promise rejected with
TypeError
If
parsed
’s
origin
and
url
’s
origin
are not the
same origin
then return
a promise rejected with
TypeError
Set
url
to
parsed
Let
be
a new promise
Run the following steps
in parallel
Let
list
be the results of running
query cookies
with
url
and
options
["
name
"]
with default
null.
If
list
is failure, then
reject
with a
TypeError
Otherwise,
resolve
with
list
Return
3.3.
The
set()
method
await cookieStore .
set
name
value
await cookieStore .
set
options
Writes (creates or modifies) a cookie.
The options default to:
Path:
Domain: same as the domain of the current document or service worker’s location
No expiry date
SameSite: strict
If provided,
maxAge
is interpreted in seconds.
The
set(
name
value
method steps are:
Let
settings
be
this
’s
relevant settings object
Let
origin
be
settings
’s
origin
If
origin
is an
opaque origin
, then return
a promise rejected with
a "
SecurityError
DOMException
Let
url
be
settings
’s
creation URL
Let
be
a new promise
Run the following steps
in parallel
Let
be the result of running
set a cookie
with
url
name
value
null,
null,
",
strict
",
false, and
null.
If
is failure, then
reject
with a
TypeError
and abort these steps.
Resolve
with undefined.
Return
The
set(
options
method steps are:
Let
settings
be
this
’s
relevant settings object
Let
origin
be
settings
’s
origin
If
origin
is an
opaque origin
, then return
a promise rejected with
a "
SecurityError
DOMException
Let
url
be
settings
’s
creation URL
Let
be
a new promise
Run the following steps
in parallel
Let
be the result of running
set a cookie
with
url
options
["
name
"],
options
["
value
"],
options
["
expires
"],
options
["
domain
"],
options
["
path
"],
options
["
sameSite
"],
options
["
partitioned
"], and
options
["
maxAge
"].
If
is failure, then
reject
with a
TypeError
and abort these steps.
Resolve
with undefined.
Return
3.4.
The
delete()
method
await cookieStore .
delete
name
await cookieStore .
delete
options
Deletes (expires) a cookie with the given name or name and
optional
domain and path.
The
delete(
name
method steps are:
Let
settings
be
this
’s
relevant settings object
Let
origin
be
settings
’s
origin
If
origin
is an
opaque origin
, then return
a promise rejected with
a "
SecurityError
DOMException
Let
url
be
settings
’s
creation URL
Let
be
a new promise
Run the following steps
in parallel
Let
be the result of running
delete a cookie
with
url
name
null,
", and
true.
If
is failure, then
reject
with a
TypeError
and abort these steps.
Resolve
with undefined.
Return
The
delete(
options
method steps are:
Let
settings
be
this
’s
relevant settings object
Let
origin
be
settings
’s
origin
If
origin
is an
opaque origin
, then return
a promise rejected with
a "
SecurityError
DOMException
Let
url
be
settings
’s
creation URL
Let
be
a new promise
Run the following steps
in parallel
Let
be the result of running
delete a cookie
with
url
options
["
name
"],
options
["
domain
"],
options
["
path
"], and
options
["
partitioned
"].
If
is failure, then
reject
with a
TypeError
and abort these steps.
Resolve
with undefined.
Return
4.
The
CookieStoreManager
interface
CookieStoreManager
has an associated
registration
which is a
service worker registration
The
CookieStoreManager
interface allows
Service Workers
to subscribe to events for cookie changes. Using the
subscribe()
method is necessary to indicate that a particular
service worker registration
is interested in change events.
Exposed
=(
ServiceWorker
Window
),
SecureContext
interface
CookieStoreManager
Promise
undefined
sequence
CookieStoreGetOptions
subscriptions
);
Promise
sequence
CookieStoreGetOptions
>>
getSubscriptions
();
Promise
undefined
unsubscribe
sequence
CookieStoreGetOptions
subscriptions
);
};
4.1.
The
subscribe()
method
await
registration
. cookies .
subscriptions
Subscribe to changes to cookies. Subscriptions can use the same options as
get()
and
getAll()
, with
optional
name
and
url
properties.
Once subscribed, notifications are delivered as "
cookiechange
" events fired against the
Service Worker
’s global scope:
The
subscribe(
subscriptions
method steps are:
Let
settings
be
this
’s
relevant settings object
Let
registration
be
this
’s
registration
Let
be
a new promise
Run the following steps
in parallel
Let
subscription list
be
registration
’s associated
cookie change subscription list
For each
entry
in
subscriptions
, run these steps:
Let
name
be null.
If
entry
["
name
"]
exists
Set
name
to
entry
["
name
"].
Normalize
name
Let
url
be
registration
’s
scope url
If
entry
["
url
"]
exists
then set
url
to the result of
parsing
entry
["
url
"] with
settings
’s
API base URL
If
url
is failure or
url
does not
start with
with
registration
’s
scope url
then
reject
with a
TypeError
and abort these steps.
Let
subscription
be the
cookie change subscription
name
url
).
If
subscription list
does not already
contain
subscription
, then
append
subscription
to
subscription list
Resolve
with undefined.
Return
4.2.
The
getSubscriptions()
method
subscriptions
= await
registration
. cookies .
getSubscriptions()
This method returns a promise which resolves to a list of the cookie change subscriptions made for this Service Worker registration.
The
getSubscriptions()
method steps are:
Let
registration
be
this
’s
registration
Let
be
a new promise
Run the following steps
in parallel
Let
subscriptions
be
registration
’s associated
cookie change subscription list
Let
result
be « ».
For each
subscription
in
subscriptions
, run these steps:
Append
«[ "name" →
subscription
’s
name
, "url" →
subscription
’s
url
]» to
result
Resolve
with
result
Return
4.3.
The
unsubscribe()
method
await
registration
. cookies .
unsubscribe
subscriptions
Calling this method will stop the registered service worker from receiving previously subscribed events. The
subscriptions
argument ought to list subscriptions in the same form passed to
subscribe()
or returned from
getSubscriptions()
The
unsubscribe(
subscriptions
method steps are:
Let
settings
be
this
’s
relevant settings object
Let
registration
be
this
’s
registration
Let
be
a new promise
Run the following steps
in parallel
Let
subscription list
be
registration
’s associated
cookie change subscription list
For each
entry
in
subscriptions
, run these steps:
Let
name
be null.
If
entry
["
name
"]
exists
Set
name
to
entry
["
name
"].
Normalize
name
Let
url
be
registration
’s
scope url
If
entry
["
url
"]
exists
then set
url
to the result of
parsing
entry
["
url
"] with
settings
’s
API base URL
If
url
is failure or
url
does not
start with
registration
’s
scope url
then
reject
with a
TypeError
and abort these steps.
Let
subscription
be the
cookie change subscription
name
url
).
Remove
any
item
from
subscription list
equal to
subscription
Resolve
with undefined.
Return
4.4.
The
ServiceWorkerRegistration
interface
The
ServiceWorkerRegistration
interface is extended to give access to a
CookieStoreManager
via
which provides the interface for subscribing to cookie changes.
Exposed
=(
ServiceWorker
Window
)]
partial
interface
ServiceWorkerRegistration
SameObject
readonly
attribute
CookieStoreManager
};
Each
ServiceWorkerRegistration
has an associated
CookieStoreManager
object.
The
CookieStoreManager
’s
registration
is equal to the
ServiceWorkerRegistration
’s
service worker registration
The
getter steps are to return
this
’s associated
CookieStoreManager
object.
Subscribing to cookie changes from a Service Worker script:
self
registration
([{
name
'session-id'
}]);
Subscribing to cookie changes from a script in a window context:
navigator
serviceWorker
'sw.js'
).
then
registration
=>
registration
([{
name
'session-id'
}]);
});
5.
Event interfaces
5.1.
The
CookieChangeEvent
interface
CookieChangeEvent
is
dispatched
against
CookieStore
objects in
Window
contexts when any
script-visible
cookie changes have occurred.
Exposed
Window
SecureContext
interface
CookieChangeEvent
Event
constructor
DOMString
type
optional
CookieChangeEventInit
eventInitDict
= {});
SameObject
readonly
attribute
FrozenArray
CookieListItem
changed
SameObject
readonly
attribute
FrozenArray
CookieListItem
deleted
};
dictionary
CookieChangeEventInit
EventInit
CookieList
changed
CookieList
deleted
};
The
changed
and
deleted
attributes must return the value they were initialized to.
5.2.
The
ExtendableCookieChangeEvent
interface
An
ExtendableCookieChangeEvent
is
dispatched
against
ServiceWorkerGlobalScope
objects when any
script-visible
cookie changes have occurred which match the
Service Worker
’s
cookie change subscription list
Note:
ExtendableEvent
is used as the ancestor interface for all events in
Service Workers
so that the worker itself can be kept alive while the async operations are performed.
Exposed
ServiceWorker
interface
ExtendableCookieChangeEvent
ExtendableEvent
constructor
DOMString
type
optional
ExtendableCookieChangeEventInit
eventInitDict
= {});
SameObject
readonly
attribute
FrozenArray
CookieListItem
changed
SameObject
readonly
attribute
FrozenArray
CookieListItem
deleted
};
dictionary
ExtendableCookieChangeEventInit
ExtendableEventInit
CookieList
changed
CookieList
deleted
};
The
changed
and
deleted
attributes must return the value they were initialized to.
6.
Global interfaces
CookieStore
is accessed by script using an attribute in the global
scope in a
Window
or
ServiceWorkerGlobalScope
context.
6.1.
The
Window
interface
SecureContext
partial
interface
Window
SameObject
readonly
attribute
CookieStore
cookieStore
};
Window
has an
associated CookieStore
, which is a
CookieStore
The
cookieStore
getter steps are to return
this
’s
associated CookieStore
6.2.
The
ServiceWorkerGlobalScope
interface
partial
interface
ServiceWorkerGlobalScope
SameObject
readonly
attribute
CookieStore
cookieStore
attribute
EventHandler
oncookiechange
};
ServiceWorkerGlobalScope
has an
associated CookieStore
, which is a
CookieStore
The
cookieStore
getter steps are to return
this
’s
associated CookieStore
7.
Algorithms
To
date serialize
DOMHighResTimeStamp
millis
let
dateTime
be the date and time
millis
milliseconds after 00:00:00 UTC, 1 January 1970
(assuming that there are exactly 86,400,000 milliseconds per day),
and return a
byte sequence
corresponding to the closest
cookie-date
representation of
dateTime
according to
Cookies § Dates
7.1.
Query cookies
To
query cookies
given a
URL
url
and
scalar value string
-or-null
name
Perform the steps defined in
Cookies § Retrieval Model
to compute the "cookie-string from a given cookie store"
with
url
as
request-uri
The
cookie-string
itself is ignored, but the intermediate
cookie-list
is used in subsequent steps.
For the purposes of the steps, the
cookie-string
is being generated for a "non-HTTP" API.
Let
list
be « ».
For each
of
cookie-list
Assert:
’s
http-only-flag
is false.
If
name
is non-null:
Normalize
name
Let
cookieName
be the result of running
UTF-8 decode without BOM
on
’s
name
If
cookieName
does not equal
name
then
continue
Let
item
be the result of running
create a CookieListItem
from
Append
item
to
list
Return
list
To
create a
CookieListItem
from a
Let
name
be the result of running
UTF-8 decode without BOM
on
’s
name
Let
value
be the result of running
UTF-8 decode without BOM
on
’s
value
Return «[ "
name
" →
name
, "
value
" →
value
]».
Note:
One implementation is known to expose information beyond _name_ and _value_.
7.2.
Set a cookie
To
set a cookie
given a
URL
url
scalar value string
name
scalar value string
value
DOMHighResTimeStamp
-or-null
expires
scalar value string
-or-null
domain
scalar value string
path
string
sameSite
boolean
partitioned
, and
64-bit signed integer
-or-null
maxAge
Normalize
name
Normalize
value
If
name
or
value
contain U+003B (;), any
C0 control
character except U+0009 TAB, or U+007F DELETE, then return failure.
Note that it’s up for discussion whether these character restrictions should also apply to
expires
domain
path
, and
sameSite
as well.
[httpwg/http-extensions Issue #1593]
If
name
contains U+003D (=), then return failure.
If
name
’s
length
is 0:
If
value
contains U+003D (=), then return failure.
If
value
’s
length
is 0, then return failure.
If
value
byte-lowercased
starts with
__host-
`, `
__host-http-
`, `
__http-
`, or `
__secure-
`, then return failure.
If
name
byte-lowercased
starts with
__host-http-
` or `
__http-
`, then return failure.
Let
encodedName
be the result of
UTF-8 encoding
name
Let
encodedValue
be the result of
UTF-8 encoding
value
If the
byte sequence
length
of
encodedName
plus the
byte sequence
length
of
encodedValue
is greater than the
maximum name/value pair size
, then return failure.
Let
host
be
url
’s
host
Let
attributes
be « ».
If
domain
is non-null:
If
domain
starts with U+002E (.), then return failure.
If
name
byte-lowercased
starts with
__host-
`, then return failure.
If
domain
is not a registrable domain suffix of and is not equal to
host
, then return failure.
Let
parsedDomain
be the result of
host parsing
domain
Assert:
parsedDomain
is not failure.
Let
encodedDomain
be the result of
UTF-8 encoding
parsedDomain
If the
byte sequence
length
of
encodedDomain
is greater than the
maximum attribute value size
, then return failure.
Append
(`
Domain
`,
encodedDomain
) to
attributes
If
expires
is non-null:
If
maxAge
is non-null, then return failure.
Append
(`
Expires
`,
expires
date serialized
)) to
attributes
Otherwise, if
maxAge
is non-null, then
append
(`
Max-Age
`,
ToString
maxAge
)) to
attributes
If
path
is the empty string, then set
path
to the
serialized cookie default path
of
url
If
path
does not
start with
U+002F (/), then return failure.
If
path
is not U+002F (/), and
name
byte-lowercased
starts with
__host-
`, then return failure.
Let
encodedPath
be the result of
UTF-8 encoding
path
If the
byte sequence
length
of
encodedPath
is greater than the
maximum attribute value size
, then return failure.
Append
(`
Path
`,
encodedPath
) to
attributes
Append
(`
Secure
`, ``) to
attributes
Switch on
sameSite
none
Append
(`
SameSite
`, `
None
`) to
attributes
strict
Append
(`
SameSite
`, `
Strict
`) to
attributes
lax
Append
(`
SameSite
`, `
Lax
`) to
attributes
If
partitioned
is true,
Append
(`
Partitioned
`, ``) to
attributes
Perform the steps defined in
Cookies § Storage Model
for when the user agent "receives a cookie" with
url
as
request-uri
encodedName
as
cookie-name
encodedValue
as
cookie-value
, and
attributes
as
cookie-attribute-list
For the purposes of the steps, the newly-created cookie was received from a "non-HTTP" API.
Return success.
Note:
Storing the cookie can still fail due to requirements in
[RFC6265BIS-14]
but these steps will be considered successful.
7.3.
Delete a cookie
To
delete a cookie
given
URL
url
scalar value string
name
scalar value string
-or-null
domain
scalar value string
path
, and
boolean
partitioned
Normalize
name
Let
value
be the empty string.
If
name
’s
length
is 0, then set
value
to any non-empty
implementation-defined
string.
Return the results of running
set a cookie
with
url
name
value
null,
domain
path
strict
",
partitioned
and 0.
7.4.
Process changes
To
process cookie changes
, run the following steps:
For every
Window
window
, run the following steps:
Let
url
be
window
’s
relevant settings object
’s
creation URL
Let
changes
be the
observable changes
for
url
If
changes
is empty
, then
continue
Queue a global task
on the
DOM manipulation task source
given
window
to
fire a change event
named "
change
" with
changes
at
window
’s
CookieStore
For every
service worker registration
registration
, run the following steps:
Let
changes
be a new
set
For each
change
in the
observable changes
for
registration
’s
scope url
, run these steps:
Let
be
change
’s cookie.
For each
subscription
in
registration
’s
cookie change subscription list
, run these steps:
If
change
is not
in
the
observable changes
for
subscription
’s
url
then
continue
Let
cookieName
be the result of running
UTF-8 decode without BOM
on
’s
name
If
subscription
’s
name
is null or
cookieName
is
subscription
’s
name
then
append
change
to
changes
and
break
If
changes
is empty
, then
continue
Let
changedList
and
deletedList
be the result of running
prepare lists
from
changes
Fire a functional event
named "
cookiechange
using
ExtendableCookieChangeEvent
on
registration
with these properties:
changed
changedList
deleted
deletedList
The
observable changes
for
url
are the
set
of
cookie changes
to
in a
cookie store
which meet the requirements in step 1 of
Cookies § Retrieval Algorithm
’s steps to compute the "cookie-string from a given cookie store"
with
url
as
request-uri
, for a "non-HTTP" API.
cookie change
is a
and a type (either
changed
or
deleted
):
which is removed due to an insertion of another
with the same
name
domain
, and
path
is ignored.
A newly-created
which is not immediately evicted is considered
changed
A newly-created
which is immediately evicted is considered
deleted
which is otherwise evicted or removed is considered
deleted
To
fire a change event
named
type
with
changes
at
target
, run the following steps:
Let
event
be the result of
creating an Event
using
CookieChangeEvent
Set
event
’s
type
attribute to
type
Set
event
’s
bubbles
and
cancelable
attributes to false.
Let
changedList
and
deletedList
be the result of running
prepare lists
from
changes
Set
event
’s
changed
attribute to
changedList
Set
event
’s
deleted
attribute to
deletedList
Dispatch
event
at
target
To
prepare lists
from
changes
, run the following steps:
Let
changedList
be « ».
Let
deletedList
be « ».
For each
change
in
changes
, run these steps:
Let
item
be the result of running
create a CookieListItem
from
change
’s cookie.
If
change
’s type is
changed
, then
append
item
to
changedList
Otherwise, run these steps:
Set
item
["
value
"] to undefined.
Append
item
to
deletedList
Return
changedList
and
deletedList
8.
Security considerations
Other than cookie access from service worker contexts, this API is not intended to expose any new capabilities to the web.
8.1.
Gotcha!
Although browser cookie implementations are now evolving in the direction of better security and fewer surprising and error-prone defaults, there are at present few guarantees about cookie data security.
unsecured origins can typically overwrite cookies used on secure origins
superdomains can typically overwrite cookies seen by subdomains
cross-site scripting attacks and other script and header injection attacks can be used to forge cookies too
cookie read operations (both from script and on web servers) don’t give any indication of where the cookie came from
browsers sometimes truncate, transform or evict cookie data in surprising and counterintuitive ways
... due to reaching storage limits
... due to character encoding differences
... due to differing syntactic and semantic rules for cookies
For these reasons it is best to use caution when interpreting any cookie’s value, and never execute a cookie’s value as script, HTML, CSS, XML, PDF, or any other executable format.
8.2.
Restrict?
This API may have the unintended side-effect of making cookies easier to use and consequently encouraging their further use. If it causes their further use in
non-secure contexts
this could result in a web less safe for users. For that reason this API has been restricted to
secure contexts
only.
8.3.
Secure cookies
This section is non-normative.
This API only allows writes for
Secure
cookies to encourage better decisions around security. However the API will still allow reading non-
Secure
cookies in order to facilitate the migration to
Secure
cookies. As a side-effect, when fetching and modifying a non-
Secure
cookie with this API, the non-
Secure
cookie will automatically be modified to
Secure
8.4.
Surprises
Some existing cookie behavior (especially domain-rather-than-origin orientation,
non-secure contexts
being able to set cookies readable in
secure contexts
, and script being able to set cookies unreadable from script contexts) may be quite surprising from a web security standpoint.
Other surprises are documented in
Cookies § Introduction
- for instance, a cookie may be set for a superdomain (e.g. app.example.com may set a cookie for the whole example.com domain), and a cookie may be readable across all port numbers on a given domain name.
Further complicating this are historical differences in cookie-handling across major browsers, although some of those (e.g. port number handling) are now handled with more consistency than they once were.
8.5.
Prefixes
Where feasible the examples use the
__Host-
and
__Secure-
name prefixes which causes some current browsers to disallow overwriting from
non-secure contexts
, disallow overwriting with no
Secure
flag, and — in the case of
__Host-
— disallow overwriting with an explicit
Domain
or non-'/'
Path
attribute (effectively enforcing same-origin semantics.) These prefixes provide important security benefits in those browsers implementing Secure Cookies and degrade gracefully (i.e. the special semantics may not be enforced in other cookie APIs but the cookies work normally and the async cookies API enforces the secure semantics for write operations) in other browsers. A major goal of this API is interoperation with existing cookies, though, so a few examples have also been provided using cookie names lacking these prefixes.
Prefix rules are also enforced in write operations by this API, but may not be enforced in the same browser for other APIs. For this reason it is inadvisable to rely on their enforcement too heavily until and unless they are more broadly adopted.
8.6.
URL scoping
Although a service worker script cannot directly access cookies today, it can already use controlled rendering of in-scope HTML and script resources to inject cookie-monitoring code under the remote control of the service worker script. This means that cookie access inside the scope of the service worker is technically possible already, it’s just not very convenient.
When the service worker is scoped more narrowly than
it may still be able to read path-scoped cookies from outside its scope’s path space by successfully guessing/constructing a 404 page URL which allows IFRAME-ing and then running script inside it the same technique could expand to the whole origin, but a carefully constructed site (one where no out-of-scope pages are IFRAME-able) can actually deny this capability to a path-scoped service worker today and I was reluctant to remove that restriction without further discussion of the implications.
8.7.
Cookie aversion
To reduce complexity for developers and eliminate the need for ephemeral test cookies, this async cookies API will explicitly reject attempts to write or delete cookies when the operation would be ignored. Likewise it will explicitly reject attempts to read cookies when that operation would ignore actual cookie data and simulate an empty cookie jar. Attempts to observe cookie changes in these contexts will still "work", but won’t invoke the callback until and unless read access becomes allowed (due e.g. to changed site permissions.)
Today writing to
document.cookie
in contexts where script-initiated cookie-writing is disallowed typically is a no-op. However, many cookie-writing scripts and frameworks always write a test cookie and then check for its existence to determine whether script-initiated cookie-writing is possible.
Likewise, today reading
document.cookie
in contexts where script-initiated cookie-reading is disallowed typically returns an empty string. However, a cooperating web server can verify that server-initiated cookie-writing and cookie-reading work and report this to the script (which still sees empty string) and the script can use this information to infer that script-initiated cookie-reading is disallowed.
9.
Privacy considerations
9.1.
Clear cookies
This section is non-normative.
When a user clears cookies for an origin, the user agent needs to wipe all storage for that origin; including service workers and DOM-accessible storage for that origin. This is to prevent websites from restoring any user identifiers in persistent storage after a user initiates the action.
Acknowledgments
Thanks to Benjamin Sittler, who created the initial proposal for this API.
Many thanks to
Adam Barth,
Alex Russell,
Andrea Marchesini,
Andrew Williams,
Anne van Kesteren,
Anusha Muley,
Ayu Ishii
Ben Kelly,
Craig Francis,
Daniel Appelquist,
Daniel Murphy,
Domenic Denicola,
Elliott Sprehn,
Fagner Brack,
Idan Horowitz,
Jake Archibald,
Joel Weinberger,
Joshua Bell,
Kenneth Rohde Christiansen,
Lukasz Olejnik,
Marijn Kruisselbrink,
Mike West,
Raymond Toy,
Rupin Mittal,
Tab Atkins, and
Victor Costan
for helping craft this standard.
This standard is written by Dylan Cutler (
Google
dylancutler@google.com
).
Intellectual property rights
This Living Standard was originally developed in the W3C WICG, where it was available under the
W3C Software and Document License
Copyright © WHATWG (Apple, Google, Mozilla, Microsoft). This work is licensed under a
Creative Commons Attribution 4.0
International License
. To the extent portions of it are incorporated into source code, such
portions in the source code are licensed under the
BSD 3-Clause License
instead.
This is the Living Standard. Those
interested in the patent-review version should view the
Living Standard Review Draft
Index
Terms defined by this specification
associated CookieStore
dfn for ServiceWorkerGlobalScope
, in § 6.2
dfn for Window
, in § 6.1
changed
attribute for CookieChangeEvent
, in § 5.1
attribute for ExtendableCookieChangeEvent
, in § 5.2
dict-member for CookieChangeEventInit
, in § 5.1
dict-member for ExtendableCookieChangeEventInit
, in § 5.2
constructor(type)
constructor for CookieChangeEvent
, in § 5.1
constructor for ExtendableCookieChangeEvent
, in § 5.2
constructor(type, eventInitDict)
constructor for CookieChangeEvent
, in § 5.1
constructor for ExtendableCookieChangeEvent
, in § 5.2
, in § 2.1
cookie change
, in § 7.4
CookieChangeEvent
, in § 5.1
CookieChangeEventInit
, in § 5.1
CookieChangeEvent(type)
, in § 5.1
CookieChangeEvent(type, eventInitDict)
, in § 5.1
cookie change subscription
, in § 2.3
cookie change subscription list
, in § 2.3
CookieInit
, in § 3
CookieList
, in § 3
CookieListItem
, in § 3
, in § 4.4
CookieSameSite
, in § 3
cookie store
, in § 2.2
CookieStore
, in § 3
cookieStore
attribute for ServiceWorkerGlobalScope
, in § 6.2
attribute for Window
, in § 6.1
CookieStoreDeleteOptions
, in § 3
CookieStoreGetOptions
, in § 3
CookieStoreManager
, in § 4
create a CookieListItem
, in § 7.1
date serialize
, in § 7
delete a cookie
, in § 7.3
deleted
attribute for CookieChangeEvent
, in § 5.1
attribute for ExtendableCookieChangeEvent
, in § 5.2
dict-member for CookieChangeEventInit
, in § 5.1
dict-member for ExtendableCookieChangeEventInit
, in § 5.2
delete(name)
, in § 3.4
delete(options)
, in § 3.4
domain
dfn for cookie
, in § 2.1
dict-member for CookieInit
, in § 3
dict-member for CookieStoreDeleteOptions
, in § 3
expires
, in § 3
ExtendableCookieChangeEvent
, in § 5.2
ExtendableCookieChangeEventInit
, in § 5.2
ExtendableCookieChangeEvent(type)
, in § 5.2
ExtendableCookieChangeEvent(type, eventInitDict)
, in § 5.2
fire a change event
, in § 7.4
get()
, in § 3.1
getAll()
, in § 3.2
getAll(name)
, in § 3.2
getAll(options)
, in § 3.2
get(name)
, in § 3.1
get(options)
, in § 3.1
getSubscriptions()
, in § 4.2
http-only-flag
, in § 2.1
"lax"
, in § 3
maxAge
, in § 3
maximum attribute value size
, in § 2.1
maximum name/value pair size
, in § 2.1
name
dfn for cookie
, in § 2.1
dfn for cookie change subscription
, in § 2.3
dict-member for CookieInit
, in § 3
dict-member for CookieListItem
, in § 3
dict-member for CookieStoreDeleteOptions
, in § 3
dict-member for CookieStoreGetOptions
, in § 3
"none"
, in § 3
normalize
, in § 2.1
normalize a cookie name or value
, in § 2.1
observable changes
, in § 7.4
onchange
, in § 3
oncookiechange
, in § 6.2
partitioned
dict-member for CookieInit
, in § 3
dict-member for CookieStoreDeleteOptions
, in § 3
path
dfn for cookie
, in § 2.1
dict-member for CookieInit
, in § 3
dict-member for CookieStoreDeleteOptions
, in § 3
prepare lists
, in § 7.4
process cookie changes
, in § 7.4
query cookies
, in § 7.1
registration
, in § 4
sameSite
, in § 3
script-visible
, in § 2.1
set a cookie
, in § 7.2
set(name, value)
, in § 3.3
set(options)
, in § 3.3
"strict"
, in § 3
subscribe(subscriptions)
, in § 4.1
unsubscribe(subscriptions)
, in § 4.3
url
dfn for cookie change subscription
, in § 2.3
dict-member for CookieStoreGetOptions
, in § 3
value
dfn for cookie
, in § 2.1
dict-member for CookieInit
, in § 3
dict-member for CookieListItem
, in § 3
Terms defined by reference
[DOM]
defines the following terms:
Event
EventInit
EventTarget
bubbles
cancelable
creating an event
dispatch
document
type
[ECMA262]
defines the following terms:
ToString
[ENCODING]
defines the following terms:
UTF-8 decode without BOM
UTF-8 encode
[FETCH]
defines the following terms:
fetch
serialized cookie default path
[HR-TIME-3]
defines the following terms:
DOMHighResTimeStamp
[HTML]
defines the following terms:
EventHandler
Window
API base URL
creation URL
DOM manipulation task source
domain
in parallel
is a registrable domain suffix of or is equal to
non-secure context
opaque origin
origin
queue a global task
relevant settings object
replaceState(data, unused, url)
same origin
secure context
[INFRA]
defines the following terms:
64-bit signed integer
append
(for list)
append
(for set)
boolean
break
byte
byte sequence
byte-lowercase
c0 control
contain
continue
exist
for each
implementation-defined
is empty
(for list)
is empty
(for map)
item
length
(for byte sequence)
length
(for string)
list
remove
scalar value string
set
starts with
(for byte sequence)
starts with
(for string)
string
tuple
with default
[Service-Workers]
defines the following terms:
ExtendableEvent
ExtendableEventInit
ServiceWorkerGlobalScope
ServiceWorkerRegistration
fire a functional event
scope url
script url
service worker
service worker registration
[URL]
defines the following terms:
basic URL parser
equal
exclude fragments
host
host parser
origin
URL
[WEBIDL]
defines the following terms:
DOMException
DOMString
Exposed
FrozenArray
Promise
SameObject
SecureContext
SecurityError
TypeError
USVString
a new promise
a promise rejected with
boolean
long long
reject
resolve
sequence
this
undefined
References
Normative References
[DOM]
Anne van Kesteren.
DOM Standard
. Living Standard. URL:
[ENCODING]
Anne van Kesteren.
Encoding Standard
. Living Standard. URL:
[FETCH]
Anne van Kesteren.
Fetch Standard
. Living Standard. URL:
[HR-TIME-3]
Yoav Weiss.
High Resolution Time
. URL:
[HTML]
Anne van Kesteren; et al.
HTML Standard
. Living Standard. URL:
[INFRA]
Anne van Kesteren; Domenic Denicola.
Infra Standard
. Living Standard. URL:
[RFC6265BIS-14]
S. Bingler; M. West; J. Wilander.
Cookies: HTTP State Management Mechanism
. Internet-Draft. URL:
[Service-Workers]
Monica CHINTALA; Yoshisato Yanagisawa.
Service Workers Nightly
. URL:
[URL]
Anne van Kesteren.
URL Standard
. Living Standard. URL:
[WEBIDL]
Edgar Chen; Timothy Gu.
Web IDL Standard
. Living Standard. URL:
IDL Index
Exposed
=(
ServiceWorker
Window
),
SecureContext
interface
CookieStore
EventTarget
Promise
CookieListItem
?>
get
USVString
name
);
Promise
CookieListItem
?>
get
optional
CookieStoreGetOptions
options
= {});
Promise
CookieList
getAll
USVString
name
);
Promise
CookieList
getAll
optional
CookieStoreGetOptions
options
= {});
Promise
undefined
set
USVString
name
USVString
value
);
Promise
undefined
set
CookieInit
options
);
Promise
undefined
delete
USVString
name
);
Promise
undefined
delete
CookieStoreDeleteOptions
options
);

Exposed
Window
attribute
EventHandler
onchange
};
dictionary
CookieStoreGetOptions
USVString
name
USVString
url
};
enum
CookieSameSite
"strict"
"lax"
"none"
};
dictionary
CookieInit
required
USVString
name
required
USVString
value
DOMHighResTimeStamp
expires
null
USVString
domain
null
USVString
path
= "/";
CookieSameSite
sameSite
= "strict";
boolean
partitioned
false
long
long
maxAge
null
};
dictionary
CookieStoreDeleteOptions
required
USVString
name
USVString
domain
null
USVString
path
= "/";
boolean
partitioned
false
};
dictionary
CookieListItem
USVString
name
USVString
value
};
typedef
sequence
CookieListItem
CookieList

Exposed
=(
ServiceWorker
Window
),
SecureContext
interface
CookieStoreManager
Promise
undefined
sequence
CookieStoreGetOptions
subscriptions
);
Promise
sequence
CookieStoreGetOptions
>>
getSubscriptions
();
Promise
undefined
unsubscribe
sequence
CookieStoreGetOptions
subscriptions
);
};

Exposed
=(
ServiceWorker
Window
)]
partial
interface
ServiceWorkerRegistration
SameObject
readonly
attribute
CookieStoreManager
};

Exposed
Window
SecureContext
interface
CookieChangeEvent
Event
constructor
DOMString
type
optional
CookieChangeEventInit
eventInitDict
= {});
SameObject
readonly
attribute
FrozenArray
CookieListItem
changed
SameObject
readonly
attribute
FrozenArray
CookieListItem
deleted
};
dictionary
CookieChangeEventInit
EventInit
CookieList
changed
CookieList
deleted
};

Exposed
ServiceWorker
interface
ExtendableCookieChangeEvent
ExtendableEvent
constructor
DOMString
type
optional
ExtendableCookieChangeEventInit
eventInitDict
= {});
SameObject
readonly
attribute
FrozenArray
CookieListItem
changed
SameObject
readonly
attribute
FrozenArray
CookieListItem
deleted
};
dictionary
ExtendableCookieChangeEventInit
ExtendableEventInit
CookieList
changed
CookieList
deleted
};

SecureContext
partial
interface
Window
SameObject
readonly
attribute
CookieStore
cookieStore
};
partial
interface
ServiceWorkerGlobalScope
SameObject
readonly
attribute
CookieStore
cookieStore
attribute
EventHandler
oncookiechange
};