Browser Context: Isolated Sessions in Headless Chrome

A browser context is an isolated, incognito-like session inside one browser. How it works in Puppeteer and Playwright, tested, and what it means for captures.

By Simon Chiu · Updated

A browser context is an isolated session inside one browser process, with its own cookies, local storage, HTTP cache and permissions, so pages in different contexts cannot see each other's state.

It is the mechanism behind incognito windows, exposed to automation. Puppeteer calls it a BrowserContext, Playwright a BrowserContext too, and the Chrome DevTools Protocol Target.createBrowserContext. This entry is part of the screenshot glossary.

What it means in plain terms

Starting a browser is slow. Starting a context is fast. So instead of one browser per job, a screenshot worker keeps one headless browser running and opens a fresh context for each job. Each context behaves like a new incognito window: it starts empty, and when you close it, everything it collected is thrown away.

 one Chrome process
 ┌───────────────────────────────────────────────────────────┐
 │ context A (tenant 1)          context B (tenant 2)        │
 │ ┌───────────────────────┐     ┌───────────────────────┐   │
 │ │ cookies: session=alice│     │ cookies: (none)       │   │
 │ │ localStorage: theme   │     │ localStorage: (empty) │   │
 │ │ HTTP cache, perms     │     │ HTTP cache, perms     │   │
 │ │ page 1   page 2       │     │ page 1                │   │
 │ └───────────────────────┘     └───────────────────────┘   │
 │ shared: the process, its launch flags, its version        │
 └───────────────────────────────────────────────────────────┘

How it works technically

A context is an in-memory profile. Anything Chrome would normally write to the user data directory (cookies, IndexedDB, service workers, cache entries, granted permissions) is kept per context. Pages, popups and iframes opened from a page stay in its context.

Some things are not isolated. All contexts share the browser process, its launch flags and its version. A crash in the browser takes down every context. And a context does not hide the browser from bot detection: a new context looks like a brand-new visitor with no history.

In Playwright, the context is also where you set device emulation: viewport, deviceScaleFactor, locale, timezone, colour scheme and user agent. In Puppeteer most of these are per page instead.

How to use it

We tested isolation with two contexts on https://example.com: context A set a cookie and a localStorage value, context B read both. Context A printed { cookie: 'session=alice', theme: 'dark' }; context B printed { cookie: '', theme: null }.

Puppeteer

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const a = await browser.createBrowserContext();
  const b = await browser.createBrowserContext();

  const pageA = await a.newPage();
  await pageA.goto('https://example.com/');
  await pageA.evaluate(() => {
    document.cookie = 'session=alice; path=/';
    localStorage.setItem('theme', 'dark');
  });

  const pageB = await b.newPage();
  await pageB.goto('https://example.com/');

  const read = (p) => p.evaluate(() => ({ cookie: document.cookie, theme: localStorage.getItem('theme') }));
  console.log('A', await read(pageA));
  console.log('B', await read(pageB));

  await a.close(); // drops A's cookies, storage and cache
  await b.close();
} finally {
  await browser.close();
}

Playwright

Here the context also carries the emulation settings. The signed-in context reported navigator.language as de-DE and one cookie; the anonymous one en-US and none:

import { chromium } from 'playwright';

const browser = await chromium.launch();
try {
  const signedIn = await browser.newContext({
    viewport: { width: 1280, height: 800 },
    locale: 'de-DE',
    timezoneId: 'Europe/Berlin',
  });
  await signedIn.addCookies([
    { name: 'session', value: 'alice', domain: 'example.com', path: '/', secure: true, httpOnly: true },
  ]);
  const anonymous = await browser.newContext({ viewport: { width: 1280, height: 800 } });

  for (const [name, ctx] of [['signedIn', signedIn], ['anonymous', anonymous]]) {
    const page = await ctx.newPage();
    await page.goto('https://example.com/');
    const cookies = (await ctx.cookies()).map((c) => c.name);
    const lang = await page.evaluate(() => navigator.language);
    console.log(name, { cookies, lang });
    await page.screenshot({ path: `${name}.png` });
    await ctx.close();
  }
} finally {
  await browser.close();
}

Tested on 2026-10-15 with Node 22.18.0, [email protected] (run with headless: 'shell') and [email protected].

RenderScreenshot

You do not manage contexts in the API. Every capture starts in a fresh browser session with no cookies and no history, so one customer's state can never reach another customer's screenshot. Everything a context would hold, you send with the request: cookies and headers in network options, locale, timezone and colour scheme in browser options.

{
  "url": "https://app.example.com/dashboard",
  "browser": { "locale": "de-DE", "timezone": "Europe/Berlin" },
  "network": {
    "cookies": [
      { "name": "session", "value": "<short-lived session id>", "domain": "app.example.com", "secure": true, "http_only": true }
    ]
  }
}

This JSON was not sent to the API for this page. It uses only fields from the network and browser parameter docs.

Common mistakes

  • Using the default context for multi-tenant jobs. browser.newPage() in Puppeteer opens in the default context, which every such page shares. Cookies from one job leak into the next.
  • Forgetting to close contexts. An unclosed context keeps its pages and memory alive. See scaling Puppeteer screenshots for long-running workers.
  • Expecting a context to change the browser. Flags, version and the HeadlessChrome user agent are per process.
  • Calling createIncognitoBrowserContext. It was removed in Puppeteer v22. Use createBrowserContext.
  • Assuming a warm cache. A new context starts with an empty HTTP cache, so the first capture of a heavy page is slower. Your own result cache needs a stable cache key instead.

To capture signed-in pages without running a browser pool, get a free API key and send your cookies with the request.

Frequently asked questions

Is a browser context the same as an incognito window?
Close to it. Chrome's incognito windows are built on the same mechanism: a separate profile in memory that is discarded on close. In automation you can open many contexts at once, each with its own cookies, storage, cache and permissions.
What happened to createIncognitoBrowserContext in Puppeteer?
Puppeteer renamed it. Since v22 the method is browser.createBrowserContext(), and every new context is isolated like an incognito session.
Should I use one context per screenshot?
For captures of different users or tenants, yes: it is cheap and prevents state from leaking. For many captures of the same public site, reusing one context keeps the HTTP cache warm and can be faster.

Related in this topic

Try RenderScreenshot free

150 free credits every month, no credit card required.