7-day Trial

Obscura Headless Browser Review: Speed, Concurrency, & Total Cost

Picture of Satyam Tripathi

Try Gologin for free

Obscura is an open-source headless browser for AI agents and web scraping. It’s written in Rust, runs JavaScript in V8, and exposes a Chrome DevTools Protocol (CDP) endpoint for Playwright and Puppeteer. In testing, version 0.2.6 used less memory than Chrome.

Two results matter more for agents that stay signed in. Obscura lost its cookies after a restart, and its browser fingerprint changed on every page load. FingerprintJS, an open-source fingerprinting library, saw a different device on each page.

This Obscura review measures speed, memory, concurrency, and cost against Chrome and Lightpanda, and persistent identity against a Gologin Cloud Browser profile.

TL;DR

  • Obscura fits self-hosted work on public pages that need JavaScript, and a Gologin profile fits signed-in agents. A Gologin Cloud Browser profile kept its cookies and the same device ID across runs, and Obscura 0.2.6 kept neither.
  • Speed depends on the build. On 30 public pages loaded one at a time, Obscura’s TLS build took a median 90 s, against 41 s for Chrome’s headless shell. With 25 concurrent sessions, the no-render TLS build, which skips rendering, was faster than the shell and used far less memory. At that load, Lightpanda was faster and lighter than both.
  • Playwright mostly worked, with 3 failed calls. In Obscura 0.2.6, 18 of 21 common API calls worked. File uploads, iframe text reads, and add_cookies() failed.
  • When you self-host, proxies are most of the bill. For 25 agents working 8 hours a day, 22 days a month, servers cost about $29 a month. The agents’ residential traffic, at 0.2 GB an hour each, needs a 1,000 GB plan from a third-party proxy provider. That plan cost $2,000 to $2,200 a month when tested. Gologin’s residential traffic costs $1 per GB beyond a plan’s allowance, on Gologin Cloud Browser or with your own browser.
Obscura 0.2.6 key facts Result
Version tested 0.2.6 release archives on macOS, and the Docker image
Tested on November 4 to 10, 2026
Price Free under Apache-2.0. You pay for your own servers and proxies. Obscura Cloud had no self-serve sign-up when tested
Cost per browser hour and per session About $0.41 to $0.45 an hour at $2 to $2.20 per GB of residential traffic. A 30-page session costs $0.10 to $0.15, almost all of it traffic
Cold start to first page load 0.42 s, against 0.43 s for Chrome’s headless shell and 0.38 s for Lightpanda, median of 5 runs
Wall time for 30 public pages, median of 3 runs TLS build 90 s, no-render TLS build 52 s, Chrome’s headless shell 41 s, Lightpanda 36 s
Concurrent sessions No plan limit when self-hosted. One worker allowed 128 CDP connections by default, and --workers adds worker processes
Peak memory, 25 concurrent sessions 2.4 GB for the TLS build on 1 worker and 2.7 GB on 8 workers. The no-render TLS build peaked at 0.8 GB, the headless shell at 4.3 GB, and Lightpanda at 0.6 GB
Session persistence After a restart, localStorage persisted in server mode. Cookies persisted only through a workaround in your own code
Anti-bot test, 5 sites, logged out Passed 2 out of 5 sites from the home IP and 3 out of 5 through a US residential proxy. Chrome 156 with a desktop user agent passed 3 out of 5 on both. Obscura loaded 24 of 50 pages, against 32 for Chrome
Playwright compatibility 18 of 21 common API calls worked. File uploads, iframe text reads, and add_cookies() failed
Proxies and geo-targeting Bring your own. Country, city, and ZIP come from your provider, with one proxy per Obscura instance
CAPTCHA solving Not built in. Obscura’s docs listed CAPTCHAs among the limits of its Chrome-TLS mode
Live view and session recording Self-hosted and view-only. Recording isn’t built in
Time to first session (setup) No sign-up or API key. Across 3 fresh downloads, the first obscura fetch after each unpack loaded a page in a median 0.9 s

What Obscura is and who it’s for

Obscura parses HTML into its own DOM. Its rendering builds then compute the layout and paint pages on the CPU. A built-in Model Context Protocol (MCP) server exposes browser tools to AI agents.

Each release archive contains a different build, and the build decides what Obscura can capture and what a site sees.

Release archive in 0.2.6 Screenshots and PDFs Chrome-style TLS and HTTP/2 fingerprint
Default archive Yes No
TLS archive Yes Yes
-no-render archive No No
No-render TLS archive No Yes
Docker image Yes No, built without Chrome-style TLS

In this review, the TLS build is the archive with Chrome-style TLS and rendering, and the no-render TLS build is the one with Chrome-style TLS and no rendering. Pick one of these 2. Use the TLS build if you need screenshots, and the no-render TLS build for DOM and text extraction. Then start it with the flag from Obscura’s README that turns on its Chrome-TLS mode, the Chrome-style TLS and the tracker blocklist.

The tests below use these 2 archives unless a table says otherwise.

Obscura Cloud, the hosted browser as a service, had no self-serve sign-up during testing, so every test here used the open-source engine.

Obscura fits stateless pipelines that scrape public pages. Its homepage listed “zero state” as a feature, meaning every session is a new sandbox with nothing shared. An agent that works inside signed-in accounts needs the opposite, a persistent identity across runs.

The right browser depends on how much rendering a page needs, whether you run your own servers, and whether your agent signs in.

Your workload Use
Public pages whose data is in the server-rendered HTML A plain HTTP client
Public pages that need JavaScript, on your own servers Lightpanda, Chrome’s headless shell with resource blocking, or Obscura’s no-render TLS build if your selectors don’t depend on element visibility
Pages an agent clicks, types, or scrolls through, without running your own servers Gologin Cloud Browser, a full Chromium browser in the cloud
Agents that return to many signed-in accounts A Gologin Cloud Browser profile per account, which keeps cookies, device fingerprint, and proxy together

Obscura review: test setup

We ran every test between November 4 and 10, 2026. Every local benchmark ran on the same Apple M3 Mac, with 8 cores and 16 GB of RAM, over the same home connection in India. Playwright 1.64.1 for Python controlled Obscura 0.2.6, Chrome 156, Chrome’s headless shell 156, and Lightpanda 1.0.2 through the same scripts.

Chrome 156 ran in new headless mode, which runs the full browser without a window. Chrome’s headless shell (chrome-headless-shell) is a separate, lightweight build of Chrome’s old headless mode, and Playwright 1.64.1 used it by default for headless runs.

The 30 public pages, such as Wikipedia, MDN, and BBC News, are the set from our Lightpanda review. FingerprintJS 5.3.0, an open-source library, converted each page’s fingerprint signals into a hashed device ID. The Gologin tests used Cloud Browser profiles, each with a Gologin residential proxy in the US.

Obscura session persistence and fingerprint consistency

Within a session, Obscura kept cookies across page loads. After Obscura restarted, though, sites no longer received the auth cookie that kept an agent signed in. Obscura’s docs said --storage-dir saves cookies and localStorage so later runs “start logged in”, but only localStorage persisted. In 0.2.6, server mode (obscura serve), which Playwright uses, lost even persistent cookies, such as a 7-day sign-in cookie. The MCP server and the obscura fetch CLI command lost them too.

In server mode, the 0.2.6 code shows the cause. Each browser context worked on its own copy of the cookie jar, while Obscura saved the original, which stayed empty. Check Obscura’s release notes for a fix before you rely on cookie persistence.

Diagram of Obscura 0.2.6 in server mode. The site’s cookie is stored in the context’s copy of the cookie jar, which isn’t saved. Obscura saves its original jar, which is empty, to cookies.json, and the next run loads that empty file.

In Obscura 0.2.6 server mode, the saved cookie file stayed empty, so the agent was signed out after a restart.

Restoring cookies from code didn’t help either. Cookies set through Playwright’s add_cookies() or the CDP cookie commands weren’t sent to the site. The one workaround that worked reads the cookies with context.cookies() before closing. After a restart, open each cookie’s domain and restore the cookie with document.cookie. That method can’t set the HttpOnly flag, so any script on the page, including third-party scripts, can then read the auth cookie.

Fingerprint changes between page loads

Obscura’s page bootstrap script generated a new random fingerprint seed for each page, and the code had no setting to keep it fixed. Between loads, 3 or 4 of FingerprintJS’s 42 signals changed, such as audio, CPU core count, and screen size. A physical device keeps those values constant between page loads, so a site that compares loads inside one signed-in session can detect the change. On the Gologin profile, all 42 signals stayed the same, including audio, screen size, and CPU core count.

Consistency check results

Our script, consistency_check.py, tests both problems. It loads a page 3 times and checks whether the device ID stays the same, plus 4 other signals that sites compare. A second run reports what the first run persisted. Local Chrome 156 passed all 5 checks.

After a restart, the second Obscura run printed:

FAIL  same device on all 3 loads
PASS  fetch() requests send the page's user agent
FAIL  fetch() requests send Sec-Fetch-Mode: cors
FAIL  timezone matches the exit IP
FAIL  WebGL available
from an earlier run: localStorage 13:39:32, cookie none
device IDs: 44bf741b, a995ea21, fb4407b8

The second Gologin run printed:

PASS  same device on all 3 loads
PASS  fetch() requests send the page's user agent
PASS  fetch() requests send Sec-Fetch-Mode: cors
PASS  timezone matches the exit IP
PASS  WebGL available
from an earlier run: localStorage 13:43:59, cookie 13:43:59
device IDs: cf31d055, cf31d055, cf31d055

You can fix one of Obscura’s 4 failures with an environment variable. The timezone check failed because Obscura 0.2.6 used the Europe/Berlin timezone by default. OBSCURA_TIMEZONE sets a zone that matches your proxy’s location. The other 3 had no setting to fix them in 0.2.6. WebGL wasn’t available, the fingerprint changed on every page, and fetch() requests were sent with Sec-Fetch-Mode: navigate.

On its second run, the Gologin profile restored its cookie and localStorage and kept the same fingerprint. It also matched its timezone to the proxy’s exit IP automatically. Gologin keeps a profile until you delete it.

Obscura review: speed, concurrency, and Playwright compatibility

Obscura’s two TLS builds gave very different speed and memory results, and only the TLS build renders pages.

Obscura vs Chrome and Lightpanda on 30 public pages

Each browser loaded the same 30 pages sequentially, with a new browser context per page. From the home connection, 7 pages returned HTTP 403 on every Chrome setup in the table below, and 4 to 6 on Obscura and Lightpanda. They included Stack Overflow, eBay, and npm. The times include those pages. On the 23 pages every setup loaded, Chrome’s headless shell was still faster than both Obscura builds. On the pages that loaded, the TLS build returned about as much text as the headless shell.

30 pages, median of 3 runs Wall time CPU time Peak memory (RSS)
Obscura 0.2.6, TLS build 90 s 21.0 s 378 MB
Obscura 0.2.6, no-render TLS build 52 s 5.9 s 137 MB
Chrome 156, new headless mode 56 s 33.5 s 1,342 MB
Chrome’s headless shell 156 41 s 12.3 s 568 MB
Headless shell, images, fonts, and media blocked 29 s 11.6 s 508 MB
Lightpanda 1.0.2 36 s 5.0 s 187 MB

Obscura’s TLS build was the slowest setup in all 3 runs, and run-to-run variation across all setups was up to 16%. Wall times include network latency over the home connection, so CPU time shows engine cost more cleanly. The TLS build used more CPU time than the headless shell too.

Peak memory sums each process’s resident memory (RSS), which overstates Chrome’s memory use, because its processes share memory.

On Wikipedia, the TLS build’s render was close to Chrome’s. Its renders didn’t always match Chrome’s, though, which matters to an agent that reads screenshots. On MDN’s Fetch API page, Obscura rendered the search box unstyled and left the theme and language dropdown menus open over the page. It rendered the Chinese, Japanese, and Korean (CJK) language names as missing-glyph boxes, because its bundled fonts lacked those glyphs.

Two full-viewport screenshots of MDN’s Fetch API page at the same 1280 by 800 viewport. At the top, in Obscura 0.2.6’s TLS build, the search box is unstyled with an Exit search label under it, and the theme and language menus are shown open over the content, with several language names shown as empty boxes. Both areas are outlined in red. At the bottom, Chrome’s headless shell 156 shows the same page with a styled search icon, closed menus, and a table-of-contents sidebar.

The same page at the same viewport. In Obscura’s screenshot, the open menus covered the table of contents that Chrome’s headless shell showed.

The no-render TLS build is the lightweight option. Obscura’s README listed 30 MB of memory and 85 ms page loads. Obscura’s benchmark repo showed similar figures on test pages, not live sites. On these 30 live pages, the no-render TLS build peaked at 137 MB, about a quarter of the headless shell’s memory. It used about half the shell’s CPU time.

Without layout, the no-render TLS build can’t compute what’s visible on screen. On a test page, it returned text hidden with CSS, and Playwright’s is_visible() reported a hidden element as visible.

Lightpanda 1.0.2, which also skips rendering, finished faster than both Obscura builds, in 36 s. It used 187 MB of peak memory, against 137 MB for the no-render TLS build.

Startup latency

Each setup ran 5 cold starts, launching the browser and loading example.com each time. Every column shows its own median, so the step times don’t sum exactly to the total. For Obscura and Lightpanda, the total also includes 23 to 25 ms for Playwright to connect over CDP.

Setup, median of 5 runs Browser launch New context and page First page load Total
Obscura 0.2.6, TLS 199 ms 10 ms 191 ms 419 ms
Chrome’s headless shell 156 250 ms 39 ms 142 ms 432 ms
Chrome 156, new headless mode 388 ms 200 ms 141 ms 728 ms
Lightpanda 1.0.2 234 ms 3 ms 119 ms 379 ms

From a cold start, Obscura was about as fast as Chrome’s headless shell. On an already running server, opening a new context and page took 10 ms.

Concurrency on one machine

Self-hosted Obscura has no plan limit on sessions. In 0.2.6, one obscura serve worker allowed 128 concurrent CDP connections by default, a limit set by --max-connections. With the limit set to 2, a third connection got HTTP 503 (Service Unavailable). To handle more connections, raise the limit or add worker processes with the --workers flag.

In the test below, each of 25 concurrent sessions loaded an article from a fixed list of 10 Wikipedia pages. The no-render TLS and headless-shell rows are medians of 4 runs, and the TLS rows are single runs. Lightpanda’s row is the median of 3 runs on November 10.

25 concurrent sessions, same Mac Time until all 25 loaded CPU time Peak memory (RSS)
Obscura 0.2.6 TLS, 1 worker 11.4 s 48.9 s 2,361 MB
Obscura 0.2.6 TLS, 8 workers 9.2 s 38.9 s 2,743 MB
Obscura 0.2.6 no-render TLS, 1 worker 2.6 s 6.4 s 802 MB
Chrome’s headless shell, 25 contexts 3.6 s 11.3 s 4,255 MB
Lightpanda 1.0.2, 1 process 2.1 s 2.7 s 554 MB

The TLS build used 3.4 to 4.3 times the headless shell’s CPU time. The no-render TLS build used just over half the CPU time and under a fifth of the peak memory of the headless shell. In all 4 runs, the no-render TLS build was faster than the headless shell.

Lightpanda 1.0.2 loaded all 25 sessions fastest, with under half the CPU time and about 70% of the peak memory of the no-render TLS build. It allowed 16 CDP connections by default, so its run raised --cdp-max-connections to 32.

A 100-session run from the same home connection had network timeouts, so it doesn’t measure engine capacity.

Playwright compatibility

Of 21 common Playwright API calls, 18 worked on Obscura 0.2.6’s TLS build. All 21 passed on the headless shell and on a Gologin Cloud Browser session.

On Obscura, clicks, form filling, dialog handling, request interception, screenshots, and PDFs all worked. So did typing, select menus, checkboxes, hover, waiting for elements, role and shadow DOM locators, navigation, and a second page. File uploads failed, because set_input_files() threw DataTransfer is not defined. Reading text inside iframes timed out, and cookies injected with add_cookies() weren’t sent to the site. Outside those 21, set_extra_http_headers() sent no headers, and Obscura ignored per-context proxies, so each IP needed its own Obscura instance.

Obscura’s Chrome-TLS mode: what a website sees

Anti-bot systems check whether the TLS fingerprint, HTTP headers, and JavaScript fingerprint agree. We checked the default archive, the Docker image, and the TLS archive.

What the site saw from Obscura 0.2.6 Default archive and Docker image TLS archive
HTTP version on navigation HTTP/1.1 HTTP/2
TLS and HTTP/2 fingerprints Not Chrome’s Chrome’s cipher list, and the same HTTP/2 fingerprint as Chrome 156
User-Agent on navigation Chrome 143 on Windows Chrome 145 on Windows
User-Agent on the page’s own fetch() requests Chrome 145 on Linux, with no Sec-CH-UA client hints Chrome 145 on Windows
Fetch metadata on fetch() requests None Sec-Fetch-Mode: navigate, which marks fetch() requests as page navigations

The TLS archive is the safer download, because its user agents matched. It still marked fetch() requests as page navigations, while Chrome 156 sent Sec-Fetch-Mode: cors for the same requests. No build returned a WebGL context. We checked fonts on the TLS archive and the Docker image. Both reported a font list that didn’t match Windows, because Obscura rendered text with its bundled fonts.

This mode can also cause problems at sign-in. It blocked thousands of tracker domains by default, including online-metrix.net, which hosts the ThreatMetrix fraud-detection script. That script loaded in Chrome and was blocked in Obscura, so if your sites run it at sign-in or checkout, set OBSCURA_BLOCK_TRACKERS=0. Obscura’s docs also listed the limits of this mode openly, including Cloudflare interactive challenges, DataDome and Akamai active challenges, CAPTCHAs, and passive TCP/IP fingerprints.

Obscura anti-bot results: 5 public sites

Obscura’s TLS build passed 2 out of 5 sites from the home IP and 3 out of 5 through a US residential proxy. A site passed when its target data loaded in at least 3 of its 5 runs.

Each run started a new Obscura 0.2.6 server from the TLS archive and loaded 1 public page per site, logged out. Chrome 156 ran the same loads as a baseline, in new headless mode. It used a regular desktop Chrome user agent, a common production setup. A load counted as a success when the target data was on the page 6 seconds after its HTML loaded. Each browser ran from the home connection in India and through a US residential proxy. The proxy runs set the browser’s timezone to America/New_York. On the home connection, Obscura kept its default Europe/Berlin timezone, and Chrome used the Mac’s.

Public page, 5 runs each Obscura, home IP Obscura, US proxy Chrome, home IP Chrome, US proxy
Zillow search results 0 3 0 2
G2 product reviews 0 0 0 0
LinkedIn company page 5 5 5 5
Amazon product page 1 0 5 5
Hacker News front page 5 5 5 5
Sites passed, of 5 2 3 3 3

Chrome loaded 32 of its 50 pages, and Obscura’s TLS build loaded 24. On Zillow through the US proxy, Obscura loaded it 3 of 5 times and Chrome loaded it twice, so only Obscura passed it. Amazon’s challenge page stopped Obscura in 9 of 10 runs, and Chrome reached the product page in all 10. With Playwright’s default HeadlessChrome user agent, Chrome loaded only 18 of its 50 pages, mainly because LinkedIn blocked it. On G2, DataDome blocked both. Our Lightpanda review ran the same 5 sites on Lightpanda Cloud, from its own datacenter IP, and it passed 4 of 5.

These were low-volume, logged-out loads. Inside a signed-in account, sites can also compare the device fingerprint and IP with the account’s history.

Two full-viewport screenshots of the same Amazon product page through the same US residential proxy. On the left, Obscura 0.2.6’s TLS build got Amazon’s challenge page, which says Click the button below to continue shopping. On the right, Chrome 156 with a desktop user agent got the Apple AirPods Pro 2 product page with its price and an Add to cart button. The delivery location is blurred.

The same Amazon page through the same US proxy. In all 5 runs through that proxy, Obscura got this challenge page, and Chrome with a desktop user agent got the product page. The proxy’s delivery location is blurred.

Obscura pricing: the full cost of self-hosting

Obscura is free under Apache-2.0. You still pay for servers, proxies, and the engineering time to run both. The scenarios below use 3 assumptions.

  • Workload. Agents work 8 hours a day, 22 days a month, at about 100 page loads an hour.
  • Traffic. An agent uses roughly 0.2 GB of traffic an hour. On the 30 pages, Chrome and Obscura’s TLS build transferred 1.7 to 2.2 MB per page, counting the blocked pages. Those pages loaded with a cold cache, each in a new context, so an agent that revisits the same sites transfers less.
  • Servers. One AWS m7g.xlarge, with 4 vCPUs and 16 GB of RAM, should handle 25 agents at this pace and runs only during their working hours. It’s an ARM (Graviton) instance, and Obscura 0.2.6 ships Linux ARM archives. AWS listed it at $0.1632 an hour on demand in us-east-1 when tested. From our CPU time per page, 25 agents at this pace average about 0.5 vCPU. All 25 loading at the same time briefly used about 4 cores and 2.4 to 2.7 GB of RAM on our Mac. Signed-in pages can need more, so test yours on Linux before you size a server.

The monthly totals below use list prices when tested and exclude the engineering time to run servers, proxies, and upgrades. Both columns assume the same hours and traffic.

Monthly workload Obscura, self-hosted Gologin Cloud Browser
25 agents, 4,400 browser hours, 1 static ISP IP each About $96 for 1 server running 25 Obscura instances, plus 25 ISP IPs at $2.70 each for 30 days, traffic included $1,232 on Enterprise with 22 extra parallel sessions, including $880 for 880 GB of static ISP traffic
25 agents, 4,400 browser hours, 880 GB of residential traffic $2,029 to $2,229 for 1 server at $29, plus the 1,000 GB proxy plan that covers 880 GB $1,232 on Enterprise with 22 extra parallel sessions, including $880 for 880 GB of residential traffic
100 agents, 17,600 browser hours, 3,520 GB of residential traffic $7,155 to $7,859 for 4 servers costing $115 in total, plus traffic at $2 to $2.20 per GB $4,946 on Enterprise with 97 extra parallel sessions, including $3,520 for 3,520 GB of residential traffic

Each Gologin cell uses the Enterprise plan, the cheapest Gologin plan at these hours, because its extra hours cost the least. It costs $14 a month with 200 browser hours and 3 parallel sessions. The cells add extra hours at $0.07 and extra parallel sessions at $2 a month each. Any plan can add as many parallel sessions as a team needs. In the Gologin column, every GB is priced at $1, the rate beyond a plan’s allowance, as if the plan included no traffic. Gologin’s static ISP IPs use the same $1-per-GB rate, with no per-IP fee. Against residential traffic from another proxy provider, Gologin’s bill is lower at both 25 and 100 agents. Teams that want a dedicated account manager and a premium SLA can also choose a Custom plan, from 4 parallel sessions.

The per-GB range uses Decodo’s 1,000 GB plan at $2.00 and NodeMaven’s 1,000 GB plan at $2.20, as listed when tested. Both quote volume discounts on larger plans, so the 100-agent self-hosted total likely overstates the cost.

Agents that keep 1 IP per account can use static ISP proxies billed per IP instead. IPRoyal listed ISP proxies at $2.70 per IP for 30 days, with traffic included, when tested. Obscura 0.2.6 ignored per-context proxies, so the $96 row assumes 25 Obscura instances, one per IP. We ran 1 instance, not 25. An idle instance used about 20 MB, so 25 add roughly 0.5 GB. Each account then keeps a static IP, but its cookies still don’t persist across restarts, and its fingerprint still changes on every page. Self-hosted Chrome keeps cookies, but 2 profiles on one Mac shared one device ID, as the alternatives section shows.

Per browser hour, servers cost about $0.007 and residential traffic costs $0.40 to $0.44. A session that loads the benchmark’s 30 pages costs about $0.10 to $0.15 self-hosted, almost all of it traffic. That’s about $3.50 to $4.90 per 1,000 pages, or about half with resource blocking. For agents that only read public pages, Gologin’s Web Unlocker costs $0.98 per 1,000 rendered pages on its $49-a-month entry plan, with JavaScript rendering included. Blocked, challenged, and empty responses cost nothing.

The self-hosted totals also apply to Chrome’s headless shell, because its 4.3 GB peak for 25 sessions fits that server’s 16 GB. Playwright ships an ARM Linux build of the headless shell, which used less CPU per page than Obscura’s TLS build. Either browser can reduce the traffic. On the same 30 pages, blocking images, fonts, and media in Chrome halved the traffic, and so did Obscura’s no-render TLS build.

Gologin Cloud Browser puts browser hours and residential traffic on one bill, with no per-request fees. Its plans include both, with extra browser hours at $0.07 to $0.09 and traffic at $1 per GB beyond a plan’s allowance. The proxy providers above listed $2 to $2.20 per GB. Steel and Browserbase listed $10 to $12 per GB on their first paid plans when tested. A forgotten Gologin session stops billing automatically after 20 idle minutes, while an active one runs for as long as the job needs. Gologin sessions can reduce traffic the same way, by blocking images, fonts, and media through page.route() or the CDP command Network.setBlockedURLs.

Gologin’s proxies also work outside Gologin products at the same $1 per GB, so a self-hosted browser can use them too. With them, the self-hosted residential bill is about $909 at 25 agents and $3,635 at 100. Gologin Cloud Browser adds the managed browser and a profile that keeps cookies, fingerprint, and proxy together, for about $13 more per agent a month.

Obscura pros

Obscura is strongest for self-hosted work on public pages, in these 5 ways.

  • Smaller memory footprint than Chrome. Both builds used less peak memory than any Chrome setup, and the no-render TLS build used the least of all setups on sequential loads.
  • Fast startup and concurrency. Opening a new context and page on a running server took 10 ms. The no-render TLS build loaded 25 concurrent sessions faster than Chrome’s headless shell.
  • Open source, single binary. The Apache-2.0 binary has no Chrome or Node.js dependency, and it includes an MCP server and a Markdown export.
  • Secure defaults. In 0.2.6, Obscura blocked requests to private IP ranges (SSRF protection) and refused to expose the CDP endpoint beyond localhost without an auth token.
  • Chrome-style TLS in the TLS build. Its user agent also matched across navigation and fetch() requests.

Obscura limitations and when not to use it

The limits below apply to version 0.2.6, and the first two matter most for agents that return to the same accounts.

  • No cookie persistence across restarts in 0.2.6. Server, MCP, and CLI mode all lost cookies, and the server-mode workaround can’t set the HttpOnly flag.
  • A new fingerprint on every page load. In 0.2.6, Obscura’s code generated a random fingerprint seed for each page.
  • Inconsistent user agents in some builds. The default archive and the Docker image sent Linux and Windows user agents on the same page.
  • Slower than Chrome’s headless shell. The TLS build had longer wall times on live pages and used more CPU time with 25 concurrent sessions.
  • Rendering that doesn’t always match Chrome. MDN’s Fetch API page was the clearest case.
  • The no-render TLS build returns hidden text. Without layout, it can’t compute what’s visible on screen.
  • Failed Playwright calls. In 0.2.6, file uploads, iframe text reads, and add_cookies() failed, and per-context proxies were ignored.
  • No WebGL. No build returned a WebGL context, which sites can check.
  • Self-managed. You run your own servers, proxies, and upgrades, and you build your own live view if a person needs to take control.
  • Most commits from one account. On GitHub’s contributors page, one account had written most of the commits when tested, so the project depends heavily on one maintainer.

Choose Gologin Cloud Browser when your agent signs in to accounts or needs full Chromium in the cloud, with proxies and Live View built in.

Obscura alternatives: Gologin and other browsers for AI agents

The terms below come from each vendor’s pricing page and docs, as listed when tested.

Obscura 0.2.6 Lightpanda Cloud Browserbase Steel Gologin Cloud Browser
Browser engine Obscura, Rust and V8 Lightpanda, no rendering Chromium Chromium Gologin’s own Chromium build
First paid plan None. Free to self-host Builder, $19 a month Developer, $20 a month Launch, pay as you go with no monthly fee Professional, $4.50 a month, with browser hours and residential traffic included
Extra browser hour Your server cost $0.08 $0.12 $0.10 $0.07 to $0.09, by plan
Residential traffic Your provider, $2 to $2.20 per GB on 1,000 GB plans Bring your own 1 GB included, then $12 per GB $10 per GB $1 per GB beyond the plan’s allowance
What a saved profile restores Only localStorage, unless your code restores cookies. The fingerprint changes on each page No state persisted between sessions, per our Lightpanda review Cookies, localStorage, IndexedDB, and other browser data, in contexts that don’t expire Cookies, extensions, credentials, and settings, plus a proxy and user agent set through its API. Deleted after 30 days without use Cookies, localStorage, IndexedDB, device fingerprint, and proxy, in a profile kept until you delete it
Longest session on the first paid plan Set by your own setup 15 minutes per CDP connection, per its docs. Sessions closed at about 6 minutes in our Lightpanda review 6 hours 15 minutes No limit
Live view View-only, self-hosted None found Yes Yes Yes

Only the Gologin profile in this table restores the full device fingerprint, according to each vendor’s docs and our tests. A Gologin Cloud Browser profile is a persistent identity. It runs in a full Chromium browser and keeps its own device fingerprint, cookies, storage, and proxy between runs. To a site, each run looks like the same returning user, not a new device with the right password.

Gologin’s own Chromium build returns the profile’s values when a site asks about the device, while Obscura injects its values with JavaScript at page start. Sites look for values that disagree, such as the Windows user agent and non-Windows font list that Obscura’s TLS build reported. The Gologin profile reported Windows fonts, such as Calibri, with its Windows user agent, and it passed all 5 of the script’s checks.

Self-hosted Chrome with a persistent user data directory keeps cookies too. As expected, 2 such profiles on one Mac got the same device ID on FingerprintJS’s public demo page, because they run on the same hardware. To a site, those accounts look like one device. A Gologin profile gives each account its own fingerprint and proxy, and each profile kept its ID from one session to the next.

Four screenshots of FingerprintJS’s demo page. In the top row, self-hosted Chrome on one Mac shows the same visitor identifier for account A and account B, starting 12a22a92. In the bottom row, Gologin shows different identifiers for account A and account B, starting 9e1e8e1b and cf31d055.

FingerprintJS saw one device for both Chrome accounts, and it saw a separate device for each Gologin account. Account B’s Gologin profile also reported cf31d055 in a separate session.

Each profile has its own proxy, either a Gologin residential, mobile, datacenter, or static ISP IP or your own proxy, attached in one API call. Gologin’s proxies cover 195+ countries, with city-level targeting.

Existing Playwright and Puppeteer code connects with one line changed, and selectors, waits, and assertions stay as they are. All 21 Playwright calls in our test passed on a Gologin session. Open pages in the browser’s existing context, as Gologin’s examples do, because that’s where the profile lives. Gologin’s MCP server lets Claude, Cursor, or another MCP client create a profile, add a proxy, and start a session without SDK code.

Obscura 0.2.6’s viewer was view-only, at 2 frames per second. In a Gologin session, a person can take control from the Live View link to sign in or enter a verification code. The script then continues in the same session. Your agent can request the Live View link through the API when a step needs a person. You can also embed Live View in your own app, so your users sign in inside your product.

Our Kernel.sh review and Cloudflare Browser Rendering review test 2 other cloud browsers.

Run the consistency check on Obscura and Gologin

The script below, consistency_check.py, runs the same check on Obscura, Chrome, or Gologin. For Gologin, copy your API token from the dashboard. Then create a profile with a Gologin proxy, in the dashboard or through the API, and pass its profile ID:

"""Check whether a browser keeps one consistent identity: 3 page loads per run, and what a second run gets back.

uv init && uv add playwright==1.64.1 httpx
Obscura:  download the Chrome-TLS archive for your OS from github.com/h4ckf0r0day/obscura/releases
          and unpack it with tar -xzf. Start it in server mode with the Chrome-TLS flag
          from its README, --port 9222 and --storage-dir ./identity-a
          uv run python consistency_check.py obscura
Gologin:  GL_API_TOKEN=... uv run python consistency_check.py gologin <profile_id>
Chrome:   uv run python consistency_check.py chrome      (a local baseline, needs Google Chrome)
Run it again a few minutes later, after restarting Obscura, to see what each identity kept.
"""
import json
import os
import sys
import time
from urllib.parse import quote

import httpx
from playwright.sync_api import sync_playwright

# Open-source FingerprintJS, added inline so no tracker blocklist can stop it
FPJS = httpx.get("https://cdn.jsdelivr.net/npm/@fingerprintjs/fingerprintjs@5.3.0/dist/fp.min.js").text
READ = """async () => {
  const api = await (await fetch('/headers')).json();  // the page's own fetch() request
  const ip = await (await fetch('https://get.geojs.io/v1/ip/geo.json')).json();
  return {
    device_id: (await (await FingerprintJS.load()).get()).visitorId,
    navigator_ua: navigator.userAgent,
    api_ua: api.headers['User-Agent'],
    api_fetch_mode: api.headers['Sec-Fetch-Mode'] || 'missing',
    // compare UTC offsets, because zone names have aliases, such as Asia/Calcutta and Asia/Kolkata
    tz_matches_ip: (now => now.toLocaleString('en-US', {timeZone: ip.timezone}) === now.toLocaleString('en-US'))(new Date()),
    webgl: !!document.createElement('canvas').getContext('webgl'),
    saved_storage: localStorage.getItem('first_seen'),
    saved_cookie: (await (await fetch('/cookies')).json()).cookies.first_seen || null,
  };
}"""

target, end_session = sys.argv[1], lambda: None
with sync_playwright() as pw:
    if target == "chrome":
        browser = pw.chromium.launch(channel="chrome", args=["--headless=new"], headless=False)
    elif target == "obscura":
        browser = pw.chromium.connect_over_cdp("http://127.0.0.1:9222")
    else:
        token, profile = os.environ["GL_API_TOKEN"], sys.argv[2]
        browser = pw.chromium.connect_over_cdp(
            f"wss://cloudbrowser.gologin.com/connect?token={token}&profile={profile}")
        end_session = lambda: httpx.delete(f"https://api.gologin.com/browser/{profile}/web",
                                           headers={"Authorization": f"Bearer {token}"}, timeout=60)
    try:
        context = browser.contexts[0] if browser.contexts else browser.new_context()
        page = context.pages[0] if context.pages else context.new_page()
        loads = []
        for _ in range(3):
            page.goto("https://httpbin.org/html")
            page.add_script_tag(content=FPJS)
            loads.append(page.evaluate(READ))
        page.goto("https://httpbin.org/headers")  # what the server saw on a page navigation
        nav_ua = json.loads(page.inner_text("body"))["headers"]["User-Agent"]
        if not loads[0]["saved_storage"]:  # first run: save a value and a server-set cookie
            stamp = time.strftime("%H:%M:%S")
            page.evaluate(f"localStorage.setItem('first_seen', '{stamp}')")
            # a cookie that lasts 7 days, like a "remember me" sign-in, so a restart shouldn't clear it
            cookie = f"first_seen={stamp}; Max-Age=604800; Path=/; Secure"
            page.goto("https://httpbin.org/response-headers?Set-Cookie=" + quote(cookie))
        browser.close()
    finally:
        end_session()  # Gologin: ends the cloud session, and the profile saves its state

first = loads[0]
checks = {
    "same device on all 3 loads": len({load["device_id"] for load in loads}) == 1,
    "fetch() requests send the page's user agent": first["api_ua"] == first["navigator_ua"] == nav_ua,
    "fetch() requests send Sec-Fetch-Mode: cors": first["api_fetch_mode"] == "cors",
    "timezone matches the exit IP": first["tz_matches_ip"],
    "WebGL available": first["webgl"],
}
for name, ok in checks.items():
    print(f"{'PASS' if ok else 'FAIL'}  {name}")
print(f"from an earlier run: localStorage {first['saved_storage'] or 'none'}, cookie {first['saved_cookie'] or 'none'}")
print(f"device IDs: {', '.join(load['device_id'][:8] for load in loads)}")

Run it twice, a few minutes apart, and restart Obscura between runs. The script ends the Gologin session itself, and the profile saves its cookies and storage. Chrome launches with a new, temporary profile on every run. It works as a baseline for the 5 checks only, and it doesn’t test session persistence.

Final thoughts

Obscura 0.2.6 is a lightweight, open-source engine for self-hosted work on public pages. Its no-render TLS build used the least memory of any setup on sequential loads. For agents that sign in, the main limit was identity, because cookies didn’t survive a restart and the fingerprint changed on every page. A Gologin profile keeps cookies, device fingerprint, and proxy together, which is what agents that return to the same accounts need.

Sign up for Gologin Cloud Browser free, with no card, and you get an API token right away. The free plan covers both runs of the script, with residential IPs included. Then run consistency_check.py on Obscura and, with the token, on a Gologin profile. Because these results apply to 0.2.6, run it again on each new Obscura release.

FAQ

What is the Obscura headless browser?

Obscura is an open-source headless browser written in Rust. Playwright and Puppeteer control it over CDP, and V8 runs page scripts. Its rendering builds draw pages with Obscura’s own engine. It includes an MCP server for AI agents, and its hosted Obscura Cloud had no self-serve sign-up during testing.

Can a website detect a headless browser?

Yes. Anti-bot systems check whether the TLS fingerprint, headers, and JavaScript fingerprint agree. In logged-out tests on 5 sites, Chrome with Playwright’s default HeadlessChrome user agent loaded 18 of 50 pages, against 32 with a desktop user agent. Signed-in sites can also compare the device fingerprint with the account’s history. A Gologin profile keeps that fingerprint the same across runs.

What is the best headless browser for AI agents?

It depends on the pages and whether the agent signs in. For self-hosted work on public pages that need JavaScript, use Lightpanda or Chrome’s headless shell. Obscura’s no-render TLS build also fits if your selectors don’t depend on element visibility. For agents that return to signed-in accounts, use a Gologin Cloud Browser profile, which keeps cookies, device fingerprint, and proxy together.

Is Obscura free and open source?

Yes, under Apache-2.0, so the engine itself costs nothing. For 25 agents working 8 hours a day, 22 days a month, servers cost about $29 a month. The agents’ residential traffic, at 0.2 GB an hour each, needs a 1,000 GB plan from a third-party proxy provider. That plan cost $2,000 to $2,200 a month when tested. Gologin’s residential traffic costs $1 per GB beyond a plan’s allowance, on Gologin Cloud Browser or with your own browser.

Is Obscura faster than Chrome?

Mostly not. On 30 public pages loaded one at a time, Chrome’s headless shell took a median 41 s, and Obscura 0.2.6’s TLS build took 90 s. Its no-render TLS build took 52 s. With 25 concurrent sessions, though, that build loaded all 25 faster than the headless shell and used less memory.

Does Obscura work with Playwright?

Mostly. Playwright connects with connect_over_cdp(), and 18 of 21 common API calls worked. All 21 worked on Chrome’s headless shell and on a Gologin Cloud Browser session. On Obscura, clicks, forms, screenshots, and PDFs worked. File uploads, iframe text reads, and add_cookies() failed in our test, so test those calls on your own pages before you rely on 0.2.6.

Can Obscura keep an agent signed in between runs?

Not without extra code. Obscura 0.2.6 didn’t persist cookies across restarts, though localStorage persisted in server mode. A workaround restores cookies with document.cookie, but it can’t set the HttpOnly flag. A Gologin Cloud Browser profile keeps its cookies, fingerprint, and proxy between runs.

Also Read