<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ethan</title>
    <description>The latest articles on DEV Community by Ethan (@rosen).</description>
    <link>https://dev.to/rosen</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4062627%2Fe6908a4e-3cb3-4072-93e0-fe9b7f0dc12c.png</url>
      <title>DEV Community: Ethan</title>
      <link>https://dev.to/rosen</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rosen"/>
    <language>en</language>
    <item>
      <title>Anti-detect browsers in 2026: what the detection side publishes</title>
      <dc:creator>Ethan</dc:creator>
      <pubDate>Wed, 02 Sep 2026 06:34:19 +0000</pubDate>
      <link>https://dev.to/rosen/anti-detect-browsers-in-2026-what-the-detection-side-publishes-26hf</link>
      <guid>https://dev.to/rosen/anti-detect-browsers-in-2026-what-the-detection-side-publishes-26hf</guid>
      <description>&lt;p&gt;Search for "anti-detect browser" and page one is almost entirely vendor blogs. Every one of them explains fingerprinting, walks through Selenium and Playwright detection, and arrives at the same destination: their product.&lt;/p&gt;

&lt;p&gt;The detection industry publishes too, and it is much less coy. CHEQ's threat intelligence team put out a write-up in February 2026 on how they detect anti-detect browsers, including a named case study. Cloudflare shipped a behavioral detection product in July 2026 and explained the reasoning behind it. Reading those next to the marketing tells you something the marketing will not: most of what anti-detect browsers are sold on cannot be verified by anyone outside the vendor, and a couple of things that can be verified are quietly worse than advertised.&lt;/p&gt;

&lt;p&gt;This post is about which is which.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why 2026 changed the question
&lt;/h2&gt;

&lt;p&gt;On June 3, 2026, Cloudflare's CEO posted that automated traffic had passed human traffic for the first time. Cloudflare Radar, which covers roughly a fifth of all websites, put automated requests at 57.5% of HTML traffic against 42.5% from humans. He had predicted the crossover at SXSW in March 2026 and expected it by the end of 2027.&lt;/p&gt;

&lt;p&gt;That volume is mostly agents, not the classic scraper. And the response has been to stop asking "is this a bot" at the request level and start scoring sessions over time.&lt;/p&gt;

&lt;p&gt;Cloudflare's Precursor, announced July 15, 2026, is the clearest statement of it. It injects JavaScript that continuously collects behavioral signals across a session rather than at a single challenge point. Their stated reason is worth quoting almost directly: modern automation can execute JavaScript, run in real browser environments, and pass individual CAPTCHAs without raising suspicion, and what stays hard to fake is consistent human behavior over time.&lt;/p&gt;

&lt;p&gt;Note what that does to the product category. If the thing being scored is your session, the browser binary is one input among many, and it is not the input you have the least control over.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three techniques, from the people building them
&lt;/h2&gt;

&lt;p&gt;CHEQ's research post describes three complementary approaches. None of them is about canvas noise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Executable and process analysis.&lt;/strong&gt; Inspect the process name and path, then search the on-disk binary for strings: product names, automation keywords, vendor identifiers. Compare against a baseline of clean builds. Where strings are encoded, dump process memory instead. This needs client access, so it does not apply to a pure website context, but it produces very low false positives when it hits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Environment fingerprint comparison.&lt;/strong&gt; Recursively enumerate everything reachable on &lt;code&gt;window&lt;/code&gt; and the DOM, then diff it against a known-good snapshot of the browser and version the client &lt;em&gt;claims&lt;/em&gt; to be. They look for properties that should exist and do not, properties that should not exist and do, and functions that are supposed to be native but contain wrappers, polyfills or instrumentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Behavioral interception.&lt;/strong&gt; Hook every function they can find, log path, arguments and call stack, then compare which functions fire during the same flow in a real browser versus the target. Calls that only appear in the instrumented environment, especially with stack traces pointing into vendor or automation code, become detection signals.&lt;/p&gt;

&lt;p&gt;Then they close the loop: drive the same page with Playwright, Puppeteer and Selenium, record which APIs fire in which order, and turn that into a signature. Their phrasing is that the lab becomes a factory for signatures. If a live session matches the pattern a Playwright mouse move produces, it gets flagged, with no automation framework anywhere near the protected site.&lt;/p&gt;

&lt;h2&gt;
  
  
  The check you can run in thirty seconds
&lt;/h2&gt;

&lt;p&gt;The second technique has a consumer-grade version that costs you nothing.&lt;/p&gt;

&lt;p&gt;Anti-detect layers have to patch native APIs to change what they report. Patching leaves a trace, because a real native function stringifies to a specific form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In a clean browser:&lt;/span&gt;
&lt;span class="nx"&gt;HTMLCanvasElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toDataURL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;// "function toDataURL() { [native code] }"&lt;/span&gt;

&lt;span class="c1"&gt;// A wrapped implementation gives you the wrapper body,&lt;/span&gt;
&lt;span class="c1"&gt;// or a re-faked [native code] string that is subtly wrong.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Worth checking across &lt;code&gt;toDataURL&lt;/code&gt;, &lt;code&gt;getImageData&lt;/code&gt;, &lt;code&gt;WebGLRenderingContext.prototype.getParameter&lt;/code&gt;, &lt;code&gt;AudioContext&lt;/code&gt;, and &lt;code&gt;Function.prototype.toString&lt;/code&gt; itself, since a thorough implementation patches the thing you would use to inspect it.&lt;/p&gt;

&lt;p&gt;CHEQ's case study makes the point concretely. In AntBrowser they found strings in the executable pointing to &lt;code&gt;window.system.base64_encode&lt;/code&gt;, a function that has no business existing in a normal browser, and confirmed it was present in the running session with the same definition. They also found functions returning what looked like native code but had not been rewritten properly.&lt;/p&gt;

&lt;p&gt;Also check inside a Web Worker. Workers have no &lt;code&gt;window&lt;/code&gt;, &lt;code&gt;screen&lt;/code&gt; or DOM access, which means extension-level and page-level patches often do not reach them, while &lt;code&gt;Intl.DateTimeFormat().resolvedOptions().timeZone&lt;/code&gt; still resolves there. A timezone that disagrees between the main thread and a worker is a contradiction the page can read for free.&lt;/p&gt;

&lt;p&gt;None of this makes you a detection engineer. It does tell you whether a tool patches carefully or sloppily, which is more than any review score will.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for choosing a product
&lt;/h2&gt;

&lt;p&gt;Every anti-detect browser markets on detection resistance, and detection resistance is the one property nobody outside the vendor can measure.&lt;/p&gt;

&lt;p&gt;It moves weekly as detection vendors ship. It is scored per session, not per binary. It depends on your proxies, your account history and your behavior far more than on the software. And per CHEQ, it can be defeated by binary strings that have nothing to do with the fingerprint layer at all. Any comparison publishing a detection score has run a private test it cannot show you, or made the number up.&lt;/p&gt;

&lt;p&gt;What is verifiable is the boring part: free tier, entry price, automation surface, platform coverage, and whether the vendor's own documentation agrees with itself.&lt;/p&gt;

&lt;p&gt;Here is that comparison for five products, using figures from vendor documentation and pricing pages read on 2026-08-24. Prices move, so treat the date as part of the data.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Product&lt;/th&gt;
&lt;th&gt;Free tier&lt;/th&gt;
&lt;th&gt;Entry price&lt;/th&gt;
&lt;th&gt;Automation surface&lt;/th&gt;
&lt;th&gt;Platforms&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://multilogin.com/" rel="noopener noreferrer"&gt;Multilogin&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;5 profiles&lt;/td&gt;
&lt;td&gt;$7.08/mo&lt;/td&gt;
&lt;td&gt;API, ADB, Selenium, Playwright&lt;/td&gt;
&lt;td&gt;Win, macOS, Linux, plus Android cloud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://dolphin-anty.com/" rel="noopener noreferrer"&gt;Dolphin Anty&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;5 profiles&lt;/td&gt;
&lt;td&gt;$10/mo&lt;/td&gt;
&lt;td&gt;Selenium, Puppeteer, Playwright&lt;/td&gt;
&lt;td&gt;Win, macOS, Linux&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.adspower.com/" rel="noopener noreferrer"&gt;AdsPower&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2 profiles&lt;/td&gt;
&lt;td&gt;$9/mo (calculated)&lt;/td&gt;
&lt;td&gt;Local API plus RPA builder&lt;/td&gt;
&lt;td&gt;Win, macOS, Linux&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://gologin.com/" rel="noopener noreferrer"&gt;GoLogin&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;3 profiles&lt;/td&gt;
&lt;td&gt;$9/mo&lt;/td&gt;
&lt;td&gt;REST API&lt;/td&gt;
&lt;td&gt;Win, macOS, Linux&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://nativbrowser.com/" rel="noopener noreferrer"&gt;Nativ Browser&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2 profiles, no sync&lt;/td&gt;
&lt;td&gt;$49/mo&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Windows only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two things in that table are more interesting than the prices.&lt;/p&gt;

&lt;p&gt;AdsPower publishes no fixed tier price. Its pricing page runs a calculator keyed to profile and member counts, and the $9 figure circulating everywhere comes from third-party trackers rather than from AdsPower. If you are budgeting from a comparison article, you are budgeting from someone's guess.&lt;/p&gt;

&lt;p&gt;Nativ Browser is the outlier in both directions. Windows only, no automation surface at all, weakest free tier, highest entry price. It is also unbeatable per seat if you have a Windows team, at 100 users and 200 devices on a $99 plan. Scores that collapse to one number hide that kind of split, which is why the per-criterion breakdown matters more than the ranking. The full scoring and the source link for every figure is at &lt;a href="https://antidetect-browser-review.com/" rel="noopener noreferrer"&gt;antidetect-browser-review.com&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that actually affects your code
&lt;/h2&gt;

&lt;p&gt;For anyone wiring this into a pipeline, the automation surface column is the whole decision, and the differences are real.&lt;/p&gt;

&lt;p&gt;Most of these expose a local API that starts a profile and hands back a Chrome DevTools Protocol endpoint. You attach to it rather than launching your own browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;selenium&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;selenium.webdriver.chrome.options&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Options&lt;/span&gt;

&lt;span class="c1"&gt;# Start the profile via the vendor's local API first,
# which returns a debugger address like 127.0.0.1:xxxxx
&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_experimental_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;debuggerAddress&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;debugger_address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Chrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That pattern is why &lt;code&gt;undetected-chromedriver&lt;/code&gt; and &lt;code&gt;playwright-stealth&lt;/code&gt; are a different category of thing. Those patch a browser you launched. This attaches to a browser someone else hardened. Neither approach touches TLS or HTTP/2 fingerprints, which sit below where any of these frameworks operate, and neither survives the behavioral signature work described above if your automation moves the mouse the way Playwright moves the mouse.&lt;/p&gt;

&lt;p&gt;The practical read: pick on API shape, language bindings and whether you need ADB for Android, because those you can test in an afternoon on a free tier. Do not pick on a detection claim you have no way to falsify.&lt;/p&gt;

&lt;h2&gt;
  
  
  One more thing on timing
&lt;/h2&gt;

&lt;p&gt;Cloudflare is changing defaults on September 15, 2026. New domains onboarding will block Agent and Training classified bots on pages that display ads, while Search stays allowed. Existing behavior is a separate question, but the direction is set, and the categories are now behavioral rather than a single on/off toggle.&lt;/p&gt;

&lt;p&gt;If your work depends on automated browsing of any kind, the ground under it is moving faster than the review cycle of any comparison article, including this one.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Sources:&lt;/strong&gt; &lt;a href="https://cheq.ai/blog/detecting-automated-and-anti-detect-browsers-a-security-researchers-approach/" rel="noopener noreferrer"&gt;CHEQ, Detecting Automated and Anti-Detect Browsers&lt;/a&gt; (Feb 25, 2026) · &lt;a href="https://blog.cloudflare.com/introducing-precursor/" rel="noopener noreferrer"&gt;Cloudflare, Introducing Precursor&lt;/a&gt; (Jul 15, 2026) · &lt;a href="https://developers.cloudflare.com/changelog/post/2026-07-01-ai-traffic-options/" rel="noopener noreferrer"&gt;Cloudflare changelog, AI traffic options&lt;/a&gt; (Jul 1, 2026) · vendor pricing and documentation pages, read 2026-08-24.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I maintain the comparison site linked above, and some links on that site are affiliate links. The vendor links in this post go to official pages and are not affiliate links.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Anti-detect tooling has legitimate uses in ad verification, QA across isolated environments and privacy research, and it also gets used for things platforms ban. Whether a given use is allowed is a terms-of-service question, not a technical one, and no tool in that table changes the answer.&lt;/p&gt;

</description>
      <category>security</category>
      <category>automation</category>
      <category>playwright</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Should You Migrate to Temporal Yet? A Decision Framework, Not Another Tutorial</title>
      <dc:creator>Ethan</dc:creator>
      <pubDate>Thu, 20 Aug 2026 05:36:00 +0000</pubDate>
      <link>https://dev.to/rosen/should-you-migrate-to-temporal-yet-a-decision-framework-not-another-tutorial-1kg1</link>
      <guid>https://dev.to/rosen/should-you-migrate-to-temporal-yet-a-decision-framework-not-another-tutorial-1kg1</guid>
      <description>&lt;p&gt;You already know what Temporal is. Immutable, timezone-aware, fixes the thirty years of pain that came from porting java.util.Date into JavaScript in 1995. That part of the internet is covered.&lt;/p&gt;

&lt;p&gt;What's less covered: whether you should actually flip the switch on your own codebase this quarter, or wait. Here's where things stand and how to decide.&lt;/p&gt;

&lt;p&gt;Where support actually is right now&lt;/p&gt;

&lt;p&gt;Firefox shipped it by default in Firefox 139, back in May 2025. Chrome caught up in Chrome 144, January 2026. Node.js added it unflagged in Node 26, released in May 2026. Safari is the holdout, still sitting in Technology Preview behind a flag, no ship date announced.&lt;/p&gt;

&lt;p&gt;That gap matters more than the version numbers suggest. Because every iOS browser runs on WebKit under the hood, no Safari support means no iPhone or iPad support either, regardless of which browser your users think they're using. Overall coverage on caniuse sits around 69%, and nearly the entire missing third is Safari.&lt;/p&gt;

&lt;p&gt;The actual decision, not the marketing version&lt;/p&gt;

&lt;p&gt;Skip the "just adopt it, it's the future" advice. Three questions determine whether this quarter is the right time.&lt;/p&gt;

&lt;p&gt;Where does your code run. Backend-only or CLI tooling on Node 26+ can go native today, no polyfill, no bundle cost. Anything touching a browser still needs to account for Safari, full stop.&lt;/p&gt;

&lt;p&gt;How much Safari traffic do you actually have. Check your analytics before deciding anything. A dashboard used internally on company Chrome installs is a different call than a consumer app with meaningful iOS share.&lt;/p&gt;

&lt;p&gt;What's your bundle budget. The two production polyfills aren't free. @js-temporal/polyfill, maintained by the proposal's own champions, runs about 56KB minified and gzipped. temporal-polyfill from the FullCalendar team trims that to roughly 20KB by skipping BigInt internally. Neither is nothing if you're chasing Core Web Vitals.&lt;/p&gt;

&lt;p&gt;The pattern that actually ships safely&lt;/p&gt;

&lt;p&gt;Feature-detect and load conditionally, so Chrome, Firefox, and Node users never download code they don't need. Check if typeof Temporal is undefined, and if so, dynamically import temporal-polyfill/global before using it. From there, Temporal.PlainDate.from("2026-08-01") gives you a plain date, and calling .add({ months: 1 }) on it returns a brand new object a month later, the original stays untouched.&lt;/p&gt;

&lt;p&gt;Only Safari users pay the download. Everyone else runs native code with zero overhead.&lt;/p&gt;

&lt;p&gt;The gotcha nobody's tutorial mentions&lt;/p&gt;

&lt;p&gt;Every migration guide shows clean examples starting from a fresh Temporal.PlainDate.from(...). Nobody talks about what's already sitting in your database.&lt;/p&gt;

&lt;p&gt;If you're storing ISO strings or epoch milliseconds today (and you almost certainly are), the real work isn't learning the new API, it's deciding which Temporal type each existing field actually represents. A created_at timestamp is an Instant. A user's stored birthday is a PlainDate, no timezone attached. A recurring meeting time is a PlainTime combined with a timezone identifier, not a ZonedDateTime baked at creation, or your 9am meeting turns into 8am after the next DST shift.&lt;/p&gt;

&lt;p&gt;Get that mapping wrong once at the schema level and you'll be debugging timezone drift for months. Get it right, and this is the last time you'll ever have to think about it.&lt;/p&gt;

&lt;p&gt;Quick recommendation&lt;/p&gt;

&lt;p&gt;New Node-only backend service: go native now, no polyfill needed.&lt;/p&gt;

&lt;p&gt;New frontend project with low Safari traffic: ship the lightweight polyfill, drop it once Safari lands.&lt;/p&gt;

&lt;p&gt;Existing frontend with a heavy iOS user base: wait, or pilot the migration on one non-critical feature first.&lt;/p&gt;

&lt;p&gt;Anything doing date math inside a hot render loop: worth benchmarking either way, native engines have already closed most of the performance gap against userland libraries.&lt;/p&gt;

&lt;p&gt;If you've already migrated a production app, I'd genuinely like to hear what broke. The database mapping problem above is the part I haven't seen written up anywhere yet.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
