Core Concepts
HttpOnly vs Client-Side Storage
Cookies marked HttpOnly cannot be accessed or modified by client-side JavaScript (cookieStore or document.cookie). However, the browser automatically attaches active HttpOnly cookies to outgoing HTTP request headers (Cookie).
- To inspect current
HttpOnly values: Look at the Cookie request header of any outgoing HTTP request via get_network_request.
- To inspect how cookies were created or configured: Look at the
Set-Cookie response header of login/auth responses.
- To inspect non-
HttpOnly cookies: Use evaluate_script with the modern cookieStore API (async () => await cookieStore.getAll()).
Session Strategy: Live Tab vs Isolated Context
Choose the right session environment to avoid state contamination (e.g., residual analytics or auth tokens):
| Strategy |
When to Use |
Setup / Teardown |
| Live Tab (Active Page) |
Diagnosing an active user session, live 401/403 error, or current state. |
Operates directly on the currently selected page. |
Clean-Slate (isolatedContext) |
Testing cookie consent banners, first-time visits, or zero-cookie guarantees. |
Call new_page with a unique isolatedContext (e.g. "consent-audit-1"). When finished, call close_page. |
Client-Side Capabilities & Limitations
| Action |
Client JavaScript (cookieStore / document.cookie) |
DevTools Network & Context Tools |
| Read Non-HttpOnly |
✅ async () => await cookieStore.getAll() |
✅ get_network_request (Request Cookie) |
| Read HttpOnly |
❌ Blocked by browser security |
✅ get_network_request (Request Cookie) |
Inspect Attributes (Domain, Path, SameSite, Expires) |
✅ async () => await cookieStore.getAll() |
✅ get_network_request (Response Set-Cookie) |
| Modify / Delete Non-HttpOnly |
✅ async () => await cookieStore.set(...) |
N/A |
| Modify / Delete HttpOnly |
❌ Silent failure in JavaScript |
✅ Use new_page(isolatedContext: ...) for clean state |
[!WARNING]
Attempting to clear an HttpOnly cookie via JavaScript (cookieStore.delete or document.cookie = "...; max-age=0") will silently fail. To test in an unauthenticated or fresh state, always spawn a new isolated context using new_page with isolatedContext.
Workflow Patterns
1. Diagnosing Authentication Failures & Redirects (401 / 403)
When an authenticated page request fails, returns 401/403, or redirects to login:
- List Recent Requests: Call
list_network_requests with includePreservedRequests: true.
- Find the Target Request: Locate the failing request (401/403) or redirect (302/307).
- Inspect Outgoing
Cookie Header: Call get_network_request with the reqid.
- Verify if the
Cookie header was attached and whether required tokens (e.g. SESSION_ID, auth_token) were sent.
- Trigger Active Inspection (If no recent request exists):
- If the cookie was set in a previous session and no network call is listed, trigger a request:
- Use
navigate_page with reload: true, OR
- Call
evaluate_script with () => fetch(window.location.href)
- Then call
get_network_request on the new request to inspect the active Cookie header.
- Trace the Setting Request: If the cookie is missing or rejected:
- Check earlier login/handshake responses for
Set-Cookie directives:
- Path mismatch: e.g.,
Path=/api when the request is to /.
- Domain mismatch: e.g.,
Domain=api.example.com preventing cookies on sub.example.com.
- Secure flag on HTTP:
Secure cookies are never sent over unencrypted http://.
- SameSite blocking:
SameSite=Strict cookies are omitted on cross-site navigations.
- Expiration: Check if
Expires or Max-Age elapsed.
2. Cookie Banner & Consent Conformance Testing
To verify that no non-essential or tracking cookies are set before consent or when declining:
- Start Clean: Open a fresh isolated context with a dedicated name:
{"url": "<PAGE_URL>", "isolatedContext": "consent-test-1"}
- Record Baseline Cookies: Before interacting with the banner, run
evaluate_script with async () => await cookieStore.getAll().
- Inspect Premature Network Requests & Issues:
- Call
list_network_requests to ensure no third-party tracking beacons fired before consent.
- Call
list_console_messages with types: ["issue"] to check for tracking warnings.
- Interact with Consent Banner:
- Capture snapshot with
take_snapshot to locate the "Decline" or "Reject All" button uid.
- Click the button with
click.
- Verify Cookie Difference:
- Run
evaluate_script with async () => await cookieStore.getAll() after clicking to assert that only strictly necessary or consent-state cookies exist.
- Test Consent Revocation (Lifecycle Audit):
- When auditing consent withdrawal or preference changes:
- Locate and click the "Cookie Settings", "Manage Preferences", or footer privacy trigger (
take_snapshot $\rightarrow$ click).
- Deselect non-essential categories or click "Revoke All" / "Save Preferences".
- Re-query
cookieStore.getAll() to verify previously accepted non-essential cookies were cleared or expired.
- Call
list_network_requests on subsequent actions to ensure tracking beacons are no longer fired.
- Teardown Context: Call
close_page when the audit is complete to prevent leftover cookies from affecting subsequent tasks.
3. Auditing Cookie Security, SameSite & CHIPS (Partitioned Cookies)
- Fast-Track: Native DevTools Issues (Recommended):
- Deep Audit: Lighthouse Third-Party Cookies:
- Run
lighthouse_audit with mode: "navigation" and outputDirPath: "/tmp/lh-report".
- Extract the specific cookie audit without loading the full report into context:
node -e "const r=require('/tmp/lh-report/report.json'); const a=r.audits['third-party-cookies']; console.log(JSON.stringify({score: a?.score, displayValue: a?.displayValue, items: a?.details?.items}))"
4. Client-Side Cookie Inspection & Manipulation
For client-accessible, non-HttpOnly cookies (e.g., UI preferences, non-sensitive feature flags):
- Read Cookies & Attributes:
- Set / Modify Cookie:
- Set client cookie via
cookieStore:async () =>
await cookieStore.set({
name: 'theme',
value: 'dark',
expires: Date.now() + 86400000,
sameSite: 'lax',
});
- Delete Cookie:
Troubleshooting
cookieStore is undefined: cookieStore requires a Secure Context (https://, localhost, or 127.0.0.1). On non-secure HTTP origins, use () => document.cookie or test over HTTPS.
evaluate_script returns empty / unresolved Promise: cookieStore methods are asynchronous. Always wrap calls with async () => await cookieStore.getAll().
- Cookie not visible in JavaScript: The cookie is marked
HttpOnly. Trigger a network request and call get_network_request to view it in the Cookie request header.
- JavaScript deletion did not remove cookie: The cookie is
HttpOnly or requires matching Path and Domain parameters. Use a fresh isolatedContext with new_page for a clean slate.
- Cookie set in response but not sent in requests:
- Verify if page is
http:// while cookie specifies Secure.
- Check if
Domain restricts subdomains.
- Check
list_console_messages(types: ["issue"]) for browser rejection reasons.
- Residual cookies contaminating audits: Always use
new_page with a unique isolatedContext when running compliance tests, and call close_page when done.
1---2name: cookie-debugging3description: Uses Chrome DevTools MCP for inspecting, debugging, and testing cookies, session state, authentication issues, and cookie consent compliance. Use when diagnosing 401/403 errors, authentication redirects, session expiration, Cookie/Set-Cookie header issues, cookie banner consent conformance, or third-party cookie/SameSite/Partitioned cookie warnings.4---5
6## Core Concepts
7
8### HttpOnly vs Client-Side Storage
9
10Cookies marked `HttpOnly` cannot be accessed or modified by client-side JavaScript (`cookieStore` or `document.cookie`). However, the browser **automatically attaches active HttpOnly cookies to outgoing HTTP request headers (`Cookie`)**.
11
12- To inspect current `HttpOnly` values: Look at the `Cookie` request header of any outgoing HTTP request via `get_network_request`.
13- To inspect how cookies were created or configured: Look at the `Set-Cookie` response header of login/auth responses.
14- To inspect non-`HttpOnly` cookies: Use `evaluate_script` with the modern `cookieStore` API (`async () => await cookieStore.getAll()`).
15
16### Session Strategy: Live Tab vs Isolated Context
17
18Choose the right session environment to avoid state contamination (e.g., residual analytics or auth tokens):
19
20| Strategy | When to Use | Setup / Teardown |
21| :---------------------------------- | :---------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------ |
22| **Live Tab (Active Page)** | Diagnosing an active user session, live 401/403 error, or current state. | Operates directly on the currently selected page. |
23| **Clean-Slate (`isolatedContext`)** | Testing cookie consent banners, first-time visits, or zero-cookie guarantees. | Call `new_page` with a unique `isolatedContext` (e.g. `"consent-audit-1"`). When finished, call `close_page`. |
24
25### Client-Side Capabilities & Limitations
26
27| Action | Client JavaScript (`cookieStore` / `document.cookie`) | DevTools Network & Context Tools |
28| :--------------------------------------------------------------- | :---------------------------------------------------- | :------------------------------------------------------ |
29| **Read Non-HttpOnly** | ✅ `async () => await cookieStore.getAll()` | ✅ `get_network_request` (Request `Cookie`) |
30| **Read HttpOnly** | ❌ Blocked by browser security | ✅ `get_network_request` (Request `Cookie`) |
31| **Inspect Attributes** (`Domain`, `Path`, `SameSite`, `Expires`) | ✅ `async () => await cookieStore.getAll()` | ✅ `get_network_request` (Response `Set-Cookie`) |
32| **Modify / Delete Non-HttpOnly** | ✅ `async () => await cookieStore.set(...)` | N/A |
33| **Modify / Delete HttpOnly** | ❌ **Silent failure** in JavaScript | ✅ Use `new_page(isolatedContext: ...)` for clean state |
34
35> [!WARNING]
36> Attempting to clear an `HttpOnly` cookie via JavaScript (`cookieStore.delete` or `document.cookie = "...; max-age=0"`) will silently fail. To test in an unauthenticated or fresh state, always spawn a new isolated context using `new_page` with `isolatedContext`.
37
38---
39
40## Workflow Patterns
41
42### 1. Diagnosing Authentication Failures & Redirects (401 / 403)
43
44When an authenticated page request fails, returns 401/403, or redirects to login:
45
461. **List Recent Requests**: Call `list_network_requests` with `includePreservedRequests: true`.
472. **Find the Target Request**: Locate the failing request (401/403) or redirect (302/307).
483. **Inspect Outgoing `Cookie` Header**: Call `get_network_request` with the `reqid`.
49 - Verify if the `Cookie` header was attached and whether required tokens (e.g. `SESSION_ID`, `auth_token`) were sent.
504. **Trigger Active Inspection (If no recent request exists)**:
51 - If the cookie was set in a previous session and no network call is listed, trigger a request:
52 - Use `navigate_page` with `reload: true`, OR
53 - Call `evaluate_script` with `() => fetch(window.location.href)`
54 - Then call `get_network_request` on the new request to inspect the active `Cookie` header.
555. **Trace the Setting Request**: If the cookie is missing or rejected:
56 - Check earlier login/handshake responses for `Set-Cookie` directives:
57 - **Path mismatch**: e.g., `Path=/api` when the request is to `/`.
58 - **Domain mismatch**: e.g., `Domain=api.example.com` preventing cookies on `sub.example.com`.
59 - **Secure flag on HTTP**: `Secure` cookies are never sent over unencrypted `http://`.
60 - **SameSite blocking**: `SameSite=Strict` cookies are omitted on cross-site navigations.
61 - **Expiration**: Check if `Expires` or `Max-Age` elapsed.
62
63### 2. Cookie Banner & Consent Conformance Testing
64
65To verify that no non-essential or tracking cookies are set before consent or when declining:
66
671. **Start Clean**: Open a fresh isolated context with a dedicated name:
68 ```json
69 {"url": "<PAGE_URL>", "isolatedContext": "consent-test-1"}
70 ```
712. **Record Baseline Cookies**: Before interacting with the banner, run `evaluate_script` with `async () => await cookieStore.getAll()`.
723. **Inspect Premature Network Requests & Issues**:
73 - Call `list_network_requests` to ensure no third-party tracking beacons fired before consent.
74 - Call `list_console_messages` with `types: ["issue"]` to check for tracking warnings.
754. **Interact with Consent Banner**:
76 - Capture snapshot with `take_snapshot` to locate the "Decline" or "Reject All" button `uid`.
77 - Click the button with `click`.
785. **Verify Cookie Difference**:
79 - Run `evaluate_script` with `async () => await cookieStore.getAll()` after clicking to assert that only strictly necessary or consent-state cookies exist.
806. **Test Consent Revocation (Lifecycle Audit)**:
81 - When auditing consent withdrawal or preference changes:
82 - Locate and click the "Cookie Settings", "Manage Preferences", or footer privacy trigger (`take_snapshot` $\rightarrow$ `click`).
83 - Deselect non-essential categories or click "Revoke All" / "Save Preferences".
84 - Re-query `cookieStore.getAll()` to verify previously accepted non-essential cookies were cleared or expired.
85 - Call `list_network_requests` on subsequent actions to ensure tracking beacons are no longer fired.
867. **Teardown Context**: Call `close_page` when the audit is complete to prevent leftover cookies from affecting subsequent tasks.
87
88### 3. Auditing Cookie Security, SameSite & CHIPS (Partitioned Cookies)
89
901. **Fast-Track: Native DevTools Issues (Recommended)**:
91 - Call `list_console_messages` with:
92 ```json
93 {
94 "types": ["issue"],
95 "includePreservedMessages": true
96 }
97 ```
98 - Check for `CookieIssue` entries, such as:
99 - `SameSiteNoneInsecure`: `SameSite=None` without `Secure`.
100 - `ThirdPartyCookiePhaseout`: Third-party cookie blocked or restricted.
101 - `SchemefulSameSite`: Cross-scheme cookie issues.
102 - `PartitionedCookies`: Invalid CHIPS partitioning attributes.
1032. **Deep Audit: Lighthouse Third-Party Cookies**:
104 - Run `lighthouse_audit` with `mode: "navigation"` and `outputDirPath: "/tmp/lh-report"`.
105 - **Extract the specific cookie audit** without loading the full report into context:
106 ```bash
107 node -e "const r=require('/tmp/lh-report/report.json'); const a=r.audits['third-party-cookies']; console.log(JSON.stringify({score: a?.score, displayValue: a?.displayValue, items: a?.details?.items}))"
108 ```
109
110### 4. Client-Side Cookie Inspection & Manipulation
111
112For client-accessible, non-`HttpOnly` cookies (e.g., UI preferences, non-sensitive feature flags):
113
1141. **Read Cookies & Attributes**:
115 - Use the modern asynchronous Cookie Store API:
116 ```js
117 async () => await cookieStore.getAll();
118 ```
119 - _Fallback for insecure HTTP origins_: `() => document.cookie`.
1202. **Set / Modify Cookie**:
121 - Set client cookie via `cookieStore`:
122 ```js
123 async () =>
124 await cookieStore.set({
125 name: 'theme',
126 value: 'dark',
127 expires: Date.now() + 86400000,
128 sameSite: 'lax',
129 });
130 ```
1313. **Delete Cookie**:
132 - Clear client cookie:
133 ```js
134 async () => await cookieStore.delete('theme');
135 ```
136
137---
138
139## Troubleshooting
140
141- **`cookieStore` is undefined**: `cookieStore` requires a Secure Context (`https://`, `localhost`, or `127.0.0.1`). On non-secure HTTP origins, use `() => document.cookie` or test over HTTPS.
142- **`evaluate_script` returns empty / unresolved Promise**: `cookieStore` methods are asynchronous. Always wrap calls with `async () => await cookieStore.getAll()`.
143- **Cookie not visible in JavaScript**: The cookie is marked `HttpOnly`. Trigger a network request and call `get_network_request` to view it in the `Cookie` request header.
144- **JavaScript deletion did not remove cookie**: The cookie is `HttpOnly` or requires matching `Path` and `Domain` parameters. Use a fresh `isolatedContext` with `new_page` for a clean slate.
145- **Cookie set in response but not sent in requests**:
146 - Verify if page is `http://` while cookie specifies `Secure`.
147 - Check if `Domain` restricts subdomains.
148 - Check `list_console_messages(types: ["issue"])` for browser rejection reasons.
149- **Residual cookies contaminating audits**: Always use `new_page` with a unique `isolatedContext` when running compliance tests, and call `close_page` when done.