Back to Blog
Identity

Browser Cookie Management for Multi-Identity Workflows

How to manage cookies for session persistence, cross-session continuity, and per-identity isolation in automated browser workflows.

Introduction

Cookies are a cornerstone of how web browsers maintain state. They carry session tokens, user preferences, consent records, and authentication credentials across page loads and visits. When building browser profiles for privacy research, testing, or multi-account management, controlling the cookie layer is just as important as controlling the fingerprint layer. A browser with a consistent fingerprint but empty cookies looks like a fresh install every time, which is not how real browsers behave.

BotBrowser provides the --bot-cookies flag (PRO tier) to pre-load cookies at launch time. This means cookies are present before the first navigation occurs, before any JavaScript executes, and before any tracking scripts can inspect the browser state. Combined with fingerprint profiles and other identity signals, cookie management completes the picture of a realistic, persistent browser identity.

Websites and tracking systems do not evaluate cookies in isolation. They look at the relationship between cookies and other browser signals. A browser that presents a returning user's fingerprint but carries no cookies raises questions. Similarly, a browser with session cookies but a completely fresh fingerprint looks inconsistent.

For privacy researchers studying how tracking systems correlate browser signals, controlling cookies is essential. It allows you to test whether a tracking system links sessions based on cookies alone, fingerprints alone, or a combination. For multi-account management, each identity needs its own cookie store that persists across sessions to maintain login state and avoid repeated authentication flows.

Cookie management also matters for testing cookie consent banners, GDPR compliance flows, and how websites behave for returning versus new visitors. Without cookie pre-loading, every session starts as a "new visitor," making it impossible to test returning-user experiences.

Technical Background

How Browsers Store Cookies

Chromium stores cookies in a SQLite database within the user data directory. When the browser starts, it loads cookies from this database into memory. Each cookie has several attributes:

  • Domain and Path: Determine which requests include the cookie
  • Name and Value: The actual data carried by the cookie
  • Expiration: When the cookie should be deleted (session cookies have no expiration)
  • Secure flag: Whether the cookie is only sent over HTTPS
  • HttpOnly flag: Whether JavaScript can access the cookie
  • SameSite: Controls cross-site cookie behavior (Strict, Lax, or None)

When a page loads, the browser attaches matching cookies to every HTTP request. JavaScript on the page can also read and write cookies through document.cookie, subject to HttpOnly restrictions.

The Timing Problem

Most automation frameworks provide cookie management through their APIs. Playwright has context.addCookies(), and Puppeteer has page.setCookie(). However, these methods add cookies after the browser context or page has been created. This means the very first network requests, including the initial page load and any preflight requests, go out without those cookies.

This timing gap matters because some tracking systems inspect the initial request for cookie presence. If the first request is cookie-free but subsequent requests carry cookies, the inconsistency is visible in server-side logs.

BotBrowser's --bot-cookies flag solves this by injecting cookies before the browser context is fully initialized, making them available from the very first request.

Common Approaches and Their Limitations

Playwright and Puppeteer both offer cookie management through their respective APIs. These work well for many use cases, but they have limitations:

  1. Post-initialization timing: Cookies added through framework APIs are only available after context or page creation, missing the initial page load
  2. Context-scoped: Cookies are tied to a browser context, not the browser instance. Multiple contexts within the same browser instance do not share cookies unless explicitly configured
  3. Format differences: Each framework uses slightly different cookie object formats, making it harder to share cookie data between tools

Manual User Data Directory Persistence

Another approach is reusing the same --user-data-dir across sessions. This preserves cookies automatically because they persist in the SQLite database. However, it also preserves all other state: cache, local storage, IndexedDB, service workers, and browsing history. This approach gives you no granular control over which state to preserve and which to reset.

Browser extensions can manage cookies, but they run at the wrong privilege level for automation. They cannot inject cookies before the first page load, and their presence is itself a detectable signal.

BotBrowser's Approach

BotBrowser implements cookie pre-loading at the engine level through the --bot-cookies flag. This design has several advantages over the alternatives described above.

Engine-Level Injection

Cookies loaded through --bot-cookies are inserted into the browser's cookie store during initialization, before any browser context is created and before any page loads. This means:

  • The first HTTP request to any domain already carries the appropriate cookies
  • JavaScript running at page load time can read the cookies through document.cookie
  • Server-side tracking systems see a consistent cookie presence from the very first request

Flexible Input Formats

The --bot-cookies flag accepts cookie data in two ways:

Inline JSON for small cookie sets or dynamic generation:

--bot-cookies='[{"name":"session","value":"abc123","domain":".example.com"}]'

File reference using the @ prefix for larger cookie sets:

--bot-cookies="@/path/to/cookies.json"

The file should contain a JSON array of cookie objects:

[
  {
    "name": "session_id",
    "value": "abc123def456",
    "domain": ".example.com",
    "path": "/",
    "expirationDate": 1774000000,
    "secure": true,
    "httpOnly": true,
    "sameSite": "Lax"
  },
  {
    "name": "user_pref",
    "value": "dark_mode=1&lang=en",
    "domain": ".example.com",
    "path": "/",
    "expirationDate": 1774000000,
    "secure": false,
    "httpOnly": false,
    "sameSite": "None"
  }
]

Per-Identity Isolation

Each BotBrowser instance runs with its own cookie store. By pairing different cookie files with different fingerprint profiles, you create fully isolated browser identities where both the fingerprint layer and the cookie layer are independent.

Configuration and Usage

Basic CLI Usage

# Load cookies from a file
./chrome \
  --bot-profile=/path/to/profile.enc \
  --bot-cookies="@/path/to/cookies.json"

# Inline cookies
./chrome \
  --bot-profile=/path/to/profile.enc \
  --bot-cookies='[{"name":"consent","value":"granted","domain":".example.com"}]'

Playwright Integration

const { chromium } = require('playwright-core');

(async () => {
  const browser = await chromium.launch({
    executablePath: '/path/to/botbrowser/chrome',
    args: [
      '--bot-profile=/path/to/profile.enc',
      '--bot-cookies=@/path/to/cookies.json',
    ],
    headless: true,
  });

  const context = await browser.newContext();
  const page = await context.newPage();
  await page.goto('https://example.com');

  // Cookies are already present from the first request
  const cookies = await context.cookies();
  console.log('Active cookies:', cookies.length);
  await browser.close();
})();
async function createIdentity(profilePath, cookiesPath) {
  return chromium.launch({
    executablePath: '/path/to/botbrowser/chrome',
    args: [
      `--bot-profile=${profilePath}`,
      `--bot-cookies=@${cookiesPath}`,
    ],
    headless: true,
  });
}

const userA = await createIdentity('/profiles/user-a.enc', '/cookies/user-a.json');
const userB = await createIdentity('/profiles/user-b.enc', '/cookies/user-b.json');

Exporting Cookies for Session Persistence

Save cookies at the end of each session and reload them on the next launch:

// At the end of a session
const cookies = await context.cookies();
const fs = require('fs');
fs.writeFileSync('/cookies/user-a.json', JSON.stringify(cookies, null, 2));

// Next session: launch with --bot-cookies=@/cookies/user-a.json

Verification

After launching with --bot-cookies, verify that cookies are loaded correctly:

const page = await context.newPage();

// Check cookies are present before any navigation
const cookies = await context.cookies('https://example.com');
console.log('Pre-loaded cookies:', cookies.map(c => c.name));

// Navigate and verify cookies are sent with the request
await page.goto('https://example.com');

// Verify JavaScript-accessible cookies
const jsCookies = await page.evaluate(() => document.cookie);
console.log('JS-visible cookies:', jsCookies);

You can also inspect the initial request headers using your framework's network interception to confirm cookies are included from the very first request.

Cookie Loading Timeline --bot-cookies Engine init Context created Cookies ready First request Cookies sent addCookies() Too late --bot-cookies loads before context. Framework APIs load after.

Best Practices

  • Keep cookie files per identity. Never share a cookies file between different fingerprint profiles.
  • Set realistic expiration dates. Cookies with far-future expirations match normal browser behavior. Avoid dates in the past.
  • Include common consent cookies. GDPR or cookie consent tokens prevent consent banners from appearing on every visit, matching returning-user behavior.
  • Rotate cookies alongside profiles. When you rotate fingerprint profiles, update the cookie set to match the new identity.
  • Use HttpOnly and Secure flags correctly. Match the flags to what the target site actually sets in its Set-Cookie headers.
  • Use the @ prefix for file paths. This keeps your launch command clean, especially when dealing with many cookies.

Frequently Asked Questions

The flag accepts a JSON array of cookie objects. Each object should have name, value, and domain at minimum. Optional fields include path, expirationDate, secure, httpOnly, and sameSite. You can pass the JSON inline or reference a file with the @ prefix.

When are cookies available relative to page load?

Cookies loaded through --bot-cookies are available before the first page navigation. They are present in the cookie store during browser initialization, so even the very first HTTP request carries them.

Can I use --bot-cookies with Playwright's addCookies()?

Yes. Cookies from --bot-cookies are loaded first during initialization. You can add more cookies later using context.addCookies() if needed. The two methods are complementary.

How do I export cookies for reuse in the next session?

Use your automation framework to read cookies at the end of a session. With Playwright, use context.cookies(). Save the result as JSON, then pass it to --bot-cookies on the next launch.

Does --bot-cookies work with multiple browser contexts?

Cookies loaded through --bot-cookies are available to all contexts within the browser instance. If you need different cookies per context, use the framework's per-context cookie APIs for the additional cookies.

What tier is required for --bot-cookies?

The --bot-cookies flag is available at the PRO tier.

Can I combine --bot-cookies with other identity flags?

Yes. Combining --bot-cookies with --bot-bookmarks, --bot-inject-random-history, and locale/timezone flags creates a complete browser identity with consistent state across all layers.

Summary

Cookie management with --bot-cookies completes the identity layer in BotBrowser by ensuring cookies are present from the very first request. Combined with fingerprint profiles and other identity flags, it creates browser sessions that maintain consistent state across all layers.

For related topics, see Multi-Account Isolation for running multiple identities, Proxy Configuration for aligning network identity with cookies, and Profile Management for organizing profiles and cookie sets together.

#cookies#management#identity#session#privacy