One Chrome window, several isolated workspaces: how it actually works

Single-window isolation is three layers stacked: session and storage, network routing, lifecycle — not kernel virtualisation. It does not change the fingerprint.

Why opening more windows does not give you multiple accounts logged in at once

Start by dropping an intuition: a window is not a unit of isolation. Ten windows in one profile share one cookie store, one localStorage and one HTTP cache, and the site sees one session. Incognito does have a separate store, but there is only one of it and it dies with the window, so it cannot serve ten accounts. The real unit is a session container: an independent cookie set and storage namespace plus a stable ownership map. Chrome has no container API of its own — Firefox’s contextualIdentities is the exception — so an extension has to build one at the session layer: record which tab belongs to which container, then split requests and page storage along that map. CrispLogin is built this way: the container is an ownership record the extension keeps, not a Chrome primitive. The unit of isolation is the session, not the process.

Layer one: why cookies do not bleed

Accounts stay apart because of a virtual cookie jar plus per-tab request-header rewriting. Each container holds its own cookie records inside the extension. Before a request leaves, a declarativeNetRequest session rule matches the tab through condition.tabIds and sets the cookie request header to that container’s value; when the response comes back, webRequest observes Set-Cookie and writes it into the matching jar. The jar has to serialise its cookies in path-length then creation-time order, or the header will not match what the browser would have sent.

What people miss is lifetime: tabIds only works on session rules, and session rules are cleared when the browser shuts down or the extension updates. So the rules must be reinstalled on every start — skip that and the symptom is “after a restart every workspace is the same account”. Page storage is a second surface: document.cookie reads the real cookie store and localStorage has a single namespace, so a MAIN-world script has to proxy reads and writes per container. This is application-layer isolation: it separates two accounts on one site, but it does not touch the kernel fingerprint.

Layer two: how the proxy follows the workspace

Chrome has no per-tab proxy API. chrome.proxy.settings is scoped to the whole profile, and a PAC script’s FindProxyForURL(url, host) never receives a tabId; declarativeNetRequest can rewrite headers and redirect, but it has no “pick an upstream proxy” action. So what an extension can do is keep a workspace-to-egress map and rewrite that profile-wide configuration when the foreground tab changes.

PAC can split by domain, but its input is the URL, not the tab — the same site open in two workspaces gets one answer from PAC. So “ten workspaces each on its own tunnel” does not exist in an extension; what is guaranteed is “whichever workspace is in front, that is the egress”. It is also why switching workspaces shows a reconnect: change the proxy configuration and established connections are rebuilt. Boundary: this layer changes the egress IP and the request path, not the TCP/TLS fingerprint (JA3/JA4); WebRTC can be limited with chrome.privacy.network.webRTCIPHandlingPolicy to drop non-proxied UDP, but that is a browser-wide policy, not a per-workspace one.

Layer three: does the binding survive a closed tab or a restart?

The MV3 background is a service worker: 30 seconds without an event and it is terminated, taking module-level variables and any Map held in a closure with it. So “which tab belongs to which workspace” cannot live in memory; it goes into chrome.storage.session, which stays in memory while the extension is loaded and survives the worker being recycled — and is cleared when the browser restarts. Note that it is not exposed to content scripts by default; if the page side needs to read the binding, call setAccessLevel explicitly.

That splits the data in two: runtime bindings in session, and everything that must survive a restart — workspace list, proxy configuration, snapshot index — in storage.local. The causal chain is direct: because the binding lives in session rather than in a variable, a woken worker still knows which tab belongs to which workspace and will not pour one workspace’s cookies into another’s page. Keep it in memory only and the moment the worker sleeps, the next request has no owner to look up — which shows up as duplicated groups and snapshots restored into the wrong container.

Layer four: what a site snapshot stores, and what it restores

A snapshot answers a different question: portability of login state, not runtime isolation. It sits fourth in this list but is not part of isolation itself. It serialises the cookies of one site inside one container plus that site’s storage, and on restore pours them back into that container’s namespace. The two are orthogonal: isolation keeps two workspaces from interfering while both exist, a snapshot lets you return to a state without logging in again. The grain is the site, not the browser: two sites in one container are two unrelated records.

Which is why a snapshot must belong to a container — pouring A’s login state into B merges two accounts into one. It is equally important to say what it does not do: it restores application-layer session state, not a kernel-level fingerprint, and it does not promise to defeat a platform’s risk controls or device checks. Usage: Internal link: how site snapshots are used in practice.

How is this different from running several profiles?

Chrome’s multiple profiles are a physical cut at the storage-root level: each profile gets its own cookie store, storage, extensions and cache, so isolation is complete. The cost lands on four dimensions — memory, since each profile has its own network context and renderer processes, so three profiles is roughly three sets of resident cost; processes, since the process trees are separate; windows, one per profile, so switching means switching windows; fingerprint, since profiles share one kernel and one machine, so UA, Canvas and fonts are identical by default. There is a quieter cost too: bookmarks, extensions and history are not shared, so past three profiles they start to drift apart.

The extension route inverts this: one profile, one window, shared processes and kernel, in exchange for single-window parallelism at near-zero incremental cost — and the price is that it cannot reach the kernel fingerprint layer. They are not two grades of the same thing; they are two different cuts at isolation granularity. Side by side: Internal link: Chrome profiles vs. extension isolation.

Capability boundary: which layers an extension covers, and which it does not

The boundaries above, compressed into one table. Read it by asking what the platform checks: for session and egress, look at the middle column; for fingerprint consistency, look at the right one. It bears repeating: an extension does session-layer isolation, which is not the same grade of isolation as an antidetect browser — treating it as a lighter antidetect browser misjudges the risk.

Capability boundary of extension-level session isolation (as of November 2026)
LayerAn extension can coverOut of reach, or the price
Cookies and login stateSplit per container, serialisable into a snapshotThe kernel does not know containers; headers are rewritten, and losing the rules means bleeding
Page storagelocalStorage and IndexedDB can be prefixed or proxied per containerService worker, SharedWorker and other separate contexts need their own coverage
Egress IPBound to the workspace; switching the foreground switches egressOne foreground egress at a time; no concurrent tunnels
WebRTCNon-proxied UDP can be dropped (browser-wide policy)Not per workspace; media paths can still bypass the proxy
TLS fingerprintNothingJA3/JA4 come from the kernel and the network stack
Canvas / WebGL / AudioNothingRendering happens in the kernel; no extension API to rewrite it
Font enumerationNothingOnly page-level JS patches, which are detectable
HTTP cacheNothingThe cache is profile-wide and shared across containers

The table also shows where the ceiling is: what an extension replaces is the motive to buy a client just to log in separately, not the client. Which route to take is a separate question, compared point by point in Internal link: extension vs. antidetect browser.

Frequently asked questions

Is the cookie isolation an extension does the same thing as Chrome’s Partitioned Cookies?

No. Partitioned Cookies are keyed by top-level site, so one site gets one jar per top-level page; partitionKey in the cookies API is only the key for reading and writing those, and its sole component is topLevelSite — you cannot invent new partitions with it. A workspace jar is split by container, so one site can have any number of them, and the extension keeps the books. The first is browser privacy partitioning; the second is an extension-level session abstraction.

Does workspace state survive closing the tab?

Three cases. The tab is gone, so the binding goes with it. Workspace definitions — name, proxy, snapshot index — are the kind of data that has to survive a restart and normally land in storage.local. Login state is recoverable only if you saved a snapshot; otherwise you log in again. Anything in storage.session is cleared when the browser closes, so do not treat it as durable.

Does the single-window approach cost performance?

The increment comes from the tabs, not the isolation. No second browser is started and no kernel is copied; the overhead is rule matching and header rewriting, a constant amount of work per request. The real cost is the number of tabs and the pages themselves: with twenty workspaces, what stutters is rendering, not the isolation layer.

Can two workspaces use different egress IPs at the same time?

No. The proxy configuration is profile-wide, and an extension can only rewrite it when the tab changes, so at any moment only the foreground workspace uses its own egress; background workspaces are either blocked by rules or go direct. Genuine concurrent tunnels require a client with its own network stack.

Sources and last update

Where this article touches API behaviour, the official documentation governs. Extension capability boundaries move with Chrome versions — re-check before you commit.

Last updated: 11 November 2026. This describes the general implementation path for this class of extension; which APIs and storage locations a given product uses can differ, and its own documentation governs its internals.