> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/mercuryworkshop/scramjet/llms.txt
> Use this file to discover all available pages before exploring further.

# Cookie management

> How Scramjet emulates and stores cookies in proxified contexts

Scramjet provides a complete cookie emulation system that allows proxified pages to use cookies as if they were on the original domain. This guide explains how cookie management works and how to interact with it.

## How cookie emulation works

When a website is accessed through Scramjet, it's technically served from your proxy domain, not the original domain. This means normal browser cookies wouldn't work correctly. Scramjet solves this by:

1. Intercepting `Set-Cookie` headers from responses
2. Storing cookies in IndexedDB keyed by the original domain
3. Injecting stored cookies into requests to the original domain
4. Emulating `document.cookie` to work with the stored cookies

<Note>
  All cookie data is stored client-side in IndexedDB under the `$scramjet` database.
</Note>

## Cookie storage structure

Cookies are stored in IndexedDB with the following schema:

```typescript theme={null}
type Cookie = {
  name: string;
  value: string;
  path?: string;
  expires?: string;
  maxAge?: number;
  domain?: string;
  secure?: boolean;
  httpOnly?: boolean;
  sameSite?: 'strict' | 'lax' | 'none';
};
```

Each cookie is uniquely identified by:

```javascript theme={null}
const cookieId = `${cookie.domain}@${cookie.path}@${cookie.name}`;
```

## CookieStore class

The `CookieStore` class manages cookie operations:

### Setting cookies

Cookies are automatically set from HTTP response headers:

```javascript theme={null}
import { CookieStore } from '@/shared/cookie';

const cookieStore = new CookieStore();
const url = new URL('https://example.com/page');

// Set cookies from Set-Cookie headers
const setCookieHeaders = [
  'sessionId=abc123; Path=/; HttpOnly',
  'theme=dark; Path=/; Max-Age=86400',
  'consent=true; Domain=.example.com; Secure; SameSite=Lax',
];

cookieStore.setCookies(setCookieHeaders, url);
```

### Getting cookies

Retrieve cookies for a specific URL:

```javascript theme={null}
const url = new URL('https://example.com/page');

// Get cookies for JavaScript access (excludes HttpOnly cookies)
const jsCookies = cookieStore.getCookies(url, true);
console.log(jsCookies); // "sessionId=abc123; theme=dark"

// Get all cookies including HttpOnly (for HTTP requests)
const allCookies = cookieStore.getCookies(url, false);
console.log(allCookies); // "sessionId=abc123; theme=dark; consent=true"
```

<Tip>
  The `fromJs` parameter controls whether `HttpOnly` cookies are included. Set it to `true` for JavaScript access, `false` for HTTP requests.
</Tip>

### Cookie filtering

The `getCookies` method applies several filters:

<Steps>
  <Step title="Expiration check">
    Expired cookies are automatically removed:

    ```javascript theme={null}
    // Cookies with past expiration dates are filtered out
    if (cookie.expires && new Date(cookie.expires) < now) {
      // Cookie is removed from storage
    }
    ```
  </Step>

  <Step title="Secure flag check">
    Secure cookies are only sent over HTTPS:

    ```javascript theme={null}
    // Secure cookies require HTTPS
    if (cookie.secure && url.protocol !== 'https:') {
      // Cookie is excluded
    }
    ```
  </Step>

  <Step title="HttpOnly flag check">
    HttpOnly cookies are excluded from JavaScript access:

    ```javascript theme={null}
    // HttpOnly cookies are not accessible to JavaScript
    if (cookie.httpOnly && fromJs) {
      // Cookie is excluded
    }
    ```
  </Step>

  <Step title="Path matching">
    Cookies are only sent to matching paths:

    ```javascript theme={null}
    // URL path must start with cookie path
    if (!url.pathname.startsWith(cookie.path)) {
      // Cookie is excluded
    }
    ```
  </Step>

  <Step title="Domain matching">
    Cookies are only sent to matching domains:

    ```javascript theme={null}
    // Check domain match for wildcard domains
    if (cookie.domain.startsWith('.')) {
      if (!url.hostname.endsWith(cookie.domain.slice(1))) {
        // Cookie is excluded
      }
    }
    ```
  </Step>
</Steps>

## Cookie defaults

When setting cookies, Scramjet applies sensible defaults:

```javascript theme={null}
// If domain is not specified, use the current hostname
if (!cookie.domain) {
  cookie.domain = '.' + url.hostname;
}

// Ensure domain starts with a dot for wildcard matching
if (!cookie.domain.startsWith('.')) {
  cookie.domain = '.' + cookie.domain;
}

// Default path to root
if (!cookie.path) {
  cookie.path = '/';
}

// Default SameSite to Lax
if (!cookie.sameSite) {
  cookie.sameSite = 'lax';
}

// Normalize expires to string
if (cookie.expires) {
  cookie.expires = cookie.expires.toString();
}
```

## Persisting cookies

The `CookieStore` provides methods to serialize and deserialize cookie data:

```javascript theme={null}
import { CookieStore } from '@/shared/cookie';

const cookieStore = new CookieStore();

// ... set some cookies ...

// Serialize cookies to JSON string
const serialized = cookieStore.dump();
console.log(serialized); // '{"domain@path@name":{...},...]'

// Save to localStorage, IndexedDB, etc.
localStorage.setItem('cookies', serialized);

// Later, restore cookies
const restored = localStorage.getItem('cookies');
const newCookieStore = new CookieStore();
newCookieStore.load(restored);
```

## Accessing cookies in proxified pages

Inside a proxified page, `document.cookie` is automatically intercepted:

```javascript theme={null}
// In a proxified page

// Set a cookie
document.cookie = 'username=john; path=/; max-age=3600';

// Read cookies
const cookies = document.cookie;
console.log(cookies); // "username=john; sessionId=abc123"

// Parse cookies
function getCookie(name) {
  const value = `; ${document.cookie}`;
  const parts = value.split(`; ${name}=`);
  if (parts.length === 2) return parts.pop().split(';').shift();
}

const username = getCookie('username');
console.log(username); // "john"
```

## Cookie storage location

Cookies are stored in IndexedDB:

```javascript theme={null}
// Database: $scramjet
// Object Store: cookies
// Format: { key: string, value: Cookie }
```

You can inspect cookies using browser DevTools:

<Steps>
  <Step title="Open DevTools">
    Press F12 or right-click and select "Inspect"
  </Step>

  <Step title="Navigate to Application tab">
    Click the "Application" tab (Chrome) or "Storage" tab (Firefox)
  </Step>

  <Step title="Find IndexedDB">
    Expand "IndexedDB" → "\$scramjet" → "cookies"
  </Step>

  <Step title="View cookies">
    Browse the stored cookie objects
  </Step>
</Steps>

## Managing cookies programmatically

You can access the cookie database directly:

```javascript theme={null}
import { openDB } from 'idb';

// Open the Scramjet database
const db = await openDB('$scramjet', 1);

// Read all cookies
const allCookies = await db.getAll('cookies');
console.log('All cookies:', allCookies);

// Read a specific cookie
const cookieKey = '.example.com@/@sessionId';
const cookie = await db.get('cookies', cookieKey);
console.log('Cookie:', cookie);

// Delete a cookie
await db.delete('cookies', cookieKey);

// Clear all cookies
await db.clear('cookies');
```

<Warning>
  Directly manipulating the IndexedDB may cause unexpected behavior. Use with caution.
</Warning>

## Cookie attributes

### Domain attribute

Cookies can be set for specific domains:

```javascript theme={null}
// Cookie for exact domain
'session=abc; Domain=example.com'
// Stored as: .example.com
// Accessible to: example.com only

// Cookie for domain and subdomains
'session=abc; Domain=.example.com'
// Accessible to: example.com, www.example.com, api.example.com
```

### Path attribute

Cookies can be restricted to specific paths:

```javascript theme={null}
// Cookie for all paths
'token=xyz; Path=/'
// Accessible to: /page, /api/data, /anything

// Cookie for specific path
'admin=1; Path=/admin'
// Accessible to: /admin, /admin/users
// Not accessible to: /page, /api
```

### SameSite attribute

<Tabs>
  <Tab title="Strict">
    ```javascript theme={null}
    // Only sent in first-party context
    'session=abc; SameSite=Strict'
    // Not sent with cross-site requests
    ```
  </Tab>

  <Tab title="Lax">
    ```javascript theme={null}
    // Sent with top-level navigation
    'session=abc; SameSite=Lax'
    // Default value in Scramjet
    ```
  </Tab>

  <Tab title="None">
    ```javascript theme={null}
    // Sent with all requests
    'session=abc; SameSite=None; Secure'
    // Requires Secure flag
    ```
  </Tab>
</Tabs>

## Common patterns

### Session management

```javascript theme={null}
// Server sets session cookie
Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Lax; Max-Age=3600

// Cookie is automatically included in subsequent requests
fetch('https://example.com/api/user')
  .then(response => response.json())
  .then(data => console.log(data));
// Cookie: sessionId=abc123
```

### Authentication tokens

```javascript theme={null}
// Store auth token in cookie
document.cookie = `auth=Bearer_${token}; path=/; secure; max-age=${60*60*24*7}`;

// Automatically sent with requests
const response = await fetch('/api/protected');
```

### User preferences

```javascript theme={null}
// Save theme preference
document.cookie = 'theme=dark; path=/; max-age=31536000';

// Read theme on page load
function getTheme() {
  const value = `; ${document.cookie}`;
  const parts = value.split('; theme=');
  if (parts.length === 2) {
    return parts.pop().split(';').shift();
  }
  return 'light'; // default
}

const theme = getTheme();
document.body.classList.add(theme);
```

## Clearing cookies

To clear cookies for a specific site:

```javascript theme={null}
import { openDB } from 'idb';

async function clearCookiesForDomain(domain) {
  const db = await openDB('$scramjet', 1);
  const allCookies = await db.getAll('cookies');

  for (const key of Object.keys(allCookies)) {
    if (key.startsWith(domain + '@')) {
      await db.delete('cookies', key);
    }
  }
}

// Clear cookies for example.com
await clearCookiesForDomain('.example.com');
```

To clear all cookies:

```javascript theme={null}
import { openDB } from 'idb';

async function clearAllCookies() {
  const db = await openDB('$scramjet', 1);
  await db.clear('cookies');
}

await clearAllCookies();
```

## Related guides

<CardGroup cols={2}>
  <Card title="Working with frames" icon="window" href="/guides/frames">
    Learn about isolated browsing contexts
  </Card>

  <Card title="Configuration flags" icon="flag" href="/guides/configuration-flags">
    Configure Scramjet behavior
  </Card>
</CardGroup>
