Comprehensive guide for browser automation and web scraping with go-rod (Chrome DevTools Protocol) including stealth anti-bot-detection patterns.
Add this skill
npx mdskills install sickn33/go-rod-masterComprehensive browser automation guide with excellent stealth patterns, code examples, and edge cases
1---2name: go-rod-master3description: "Comprehensive guide for browser automation and web scraping with go-rod (Chrome DevTools Protocol) including stealth anti-bot-detection patterns."4risk: safe5source: https://github.com/go-rod/rod6---78# Go-Rod Browser Automation Master910## Overview1112[Rod](https://github.com/go-rod/rod) is a high-level Go driver built directly on the [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) for browser automation and web scraping. Unlike wrappers around other tools, Rod communicates with the browser natively via CDP, providing thread-safe operations, chained context design for timeouts/cancellation, auto-wait for elements, correct iframe/shadow DOM handling, and zero zombie browser processes.1314The companion library [go-rod/stealth](https://github.com/go-rod/stealth) injects anti-bot-detection evasions based on [puppeteer-extra stealth](https://github.com/nichochar/puppeteer-extra/tree/master/packages/extract-stealth-evasions), hiding headless browser fingerprints from detection systems.1516## When to Use This Skill1718- Use when the user asks to **scrape**, **automate**, or **test** a website using Go.19- Use when the user needs a **headless browser** for dynamic/SPA content (React, Vue, Angular).20- Use when the user mentions **stealth**, **anti-bot**, **avoiding detection**, **Cloudflare**, or **bot detection bypass**.21- Use when the user wants to work with the **Chrome DevTools Protocol (CDP)** directly from Go.22- Use when the user needs to **intercept** or **hijack** network requests in a browser context.23- Use when the user asks about **concurrent browser scraping** or **page pooling** in Go.24- Use when the user is migrating from **chromedp** or **Playwright Go** and wants a simpler API.2526## Safety & Risk2728**Risk Level: ๐ต Safe**2930- **Read-Only by Default:** Default behavior is navigating and reading page content (scraping/testing).31- **Isolated Contexts:** Browser contexts are sandboxed; cookies and storage do not persist unless explicitly saved.32- **Resource Cleanup:** Designed around Go's `defer` pattern โ browsers and pages close automatically.33- **No External Mutations:** Does not modify external state unless the script explicitly submits forms or POSTs data.3435## Installation3637```bash38# Core rod library39go get github.com/go-rod/rod@latest4041# Stealth anti-detection plugin (ALWAYS include for production scraping)42go get github.com/go-rod/stealth@latest43```4445Rod auto-downloads a compatible Chromium binary on first run. To pre-download:4647```bash48go run github.com/nichochar/go-rod.github.io/cmd/launcher@latest49```5051## Core Concepts5253### Browser Lifecycle5455Rod manages three layers: **Browser โ Page โ Element**.5657```go58// Launch and connect to a browser59browser := rod.New().MustConnect()60defer browser.MustClose()6162// Create a page (tab)63page := browser.MustPage("https://example.com")6465// Find an element66el := page.MustElement("h1")67fmt.Println(el.MustText())68```6970### Must vs Error Patterns7172Rod provides two API styles for every operation:7374| Style | Method | Use Case |75|:------|:-------|:---------|76| **Must** | `MustElement()`, `MustClick()`, `MustText()` | Scripting, debugging, prototyping. Panics on error. |77| **Error** | `Element()`, `Click()`, `Text()` | Production code. Returns `error` for explicit handling. |7879**Production pattern:**8081```go82el, err := page.Element("#login-btn")83if err != nil {84 return fmt.Errorf("login button not found: %w", err)85}86if err := el.Click(proto.InputMouseButtonLeft, 1); err != nil {87 return fmt.Errorf("click failed: %w", err)88}89```9091**Scripting pattern with Try:**9293```go94err := rod.Try(func() {95 page.MustElement("#login-btn").MustClick()96})97if errors.Is(err, context.DeadlineExceeded) {98 log.Println("timeout finding login button")99}100```101102### Context & Timeout103104Rod uses Go's `context.Context` for cancellation and timeouts. Context propagates recursively to all child operations.105106```go107// Set a 5-second timeout for the entire operation chain108page.Timeout(5 * time.Second).109 MustWaitLoad().110 MustElement("title").111 CancelTimeout(). // subsequent calls are not bound by the 5s timeout112 Timeout(30 * time.Second).113 MustText()114```115116### Element Selectors117118Rod supports multiple selector strategies:119120```go121// CSS selector (most common)122page.MustElement("div.content > p.intro")123124// CSS selector with text regex matching125page.MustElementR("button", "Submit|Send")126127// XPath128page.MustElementX("//div[@class='content']//p")129130// Search across iframes and shadow DOM (like DevTools Ctrl+F)131page.MustSearch(".deeply-nested-element")132```133134### Auto-Wait135136Rod automatically retries element queries until the element appears or the context times out. You do not need manual sleeps:137138```go139// This will automatically wait until the element exists140el := page.MustElement("#dynamic-content")141142// Wait until the element is stable (position/size not changing)143el.MustWaitStable().MustClick()144145// Wait until page has no pending network requests146wait := page.MustWaitRequestIdle()147page.MustElement("#search").MustInput("query")148wait()149```150151---152153## Stealth & Anti-Bot Detection (go-rod/stealth)154155> **IMPORTANT:** For any production scraping or automation against real websites, ALWAYS use `stealth.MustPage()` instead of `browser.MustPage()`. This is the single most important step for avoiding bot detection.156157### How Stealth Works158159The `go-rod/stealth` package injects JavaScript evasions into every new page that:160161- **Remove `navigator.webdriver`** โ the primary headless detection signal.162- **Spoof WebGL vendor/renderer** โ presents real GPU info (e.g., "Intel Inc." / "Intel Iris OpenGL Engine") instead of headless markers like "Google SwiftShader".163- **Fix Chrome plugin array** โ reports proper `PluginArray` type with realistic plugin count.164- **Patch permissions API** โ returns `"prompt"` instead of bot-revealing values.165- **Set realistic languages** โ reports `en-US,en` instead of empty arrays.166- **Fix broken image dimensions** โ headless browsers report 0x0; stealth fixes this to 16x16.167168### Usage169170**Creating a stealth page (recommended for all production use):**171172```go173import (174 "github.com/go-rod/rod"175 "github.com/go-rod/stealth"176)177178browser := rod.New().MustConnect()179defer browser.MustClose()180181// Use stealth.MustPage instead of browser.MustPage182page := stealth.MustPage(browser)183page.MustNavigate("https://bot.sannysoft.com")184```185186**With error handling:**187188```go189page, err := stealth.Page(browser)190if err != nil {191 return fmt.Errorf("failed to create stealth page: %w", err)192}193page.MustNavigate("https://example.com")194```195196**Using stealth.JS directly (advanced โ for custom page creation):**197198```go199// If you need to create the page yourself (e.g., with specific options),200// inject stealth.JS manually via EvalOnNewDocument201page := browser.MustPage()202page.MustEvalOnNewDocument(stealth.JS)203page.MustNavigate("https://example.com")204```205206### Verifying Stealth207208Navigate to a bot detection test page to verify evasions:209210```go211page := stealth.MustPage(browser)212page.MustNavigate("https://bot.sannysoft.com")213page.MustScreenshot("stealth_test.png")214```215216Expected results for a properly stealth-configured browser:217- **WebDriver**: `missing (passed)`218- **Chrome**: `present (passed)`219- **Plugins Length**: `3` (not `0`)220- **Languages**: `en-US,en`221222---223224## Implementation Guidelines225226### 1. Launcher Configuration227228Use the `launcher` package to customize browser launch flags:229230```go231import "github.com/go-rod/rod/lib/launcher"232233url := launcher.New().234 Headless(true). // false for debugging235 Proxy("127.0.0.1:8080"). // upstream proxy236 Set("disable-gpu", ""). // custom Chrome flag237 Delete("use-mock-keychain"). // remove a default flag238 MustLaunch()239240browser := rod.New().ControlURL(url).MustConnect()241defer browser.MustClose()242```243244**Debugging mode (visible browser + slow motion):**245246```go247l := launcher.New().248 Headless(false).249 Devtools(true)250defer l.Cleanup()251252browser := rod.New().253 ControlURL(l.MustLaunch()).254 Trace(true).255 SlowMotion(2 * time.Second).256 MustConnect()257```258259### 2. Proxy Support260261```go262// Set proxy at launch263url := launcher.New().264 Proxy("socks5://127.0.0.1:1080").265 MustLaunch()266267browser := rod.New().ControlURL(url).MustConnect()268269// Handle proxy authentication270go browser.MustHandleAuth("username", "password")()271272// Ignore SSL certificate errors (for MITM proxies)273browser.MustIgnoreCertErrors(true)274```275276### 3. Input Simulation277278```go279import "github.com/go-rod/rod/lib/input"280281// Type into an input field (replaces existing value)282page.MustElement("#email").MustInput("user@example.com")283284// Simulate keyboard keys285page.Keyboard.MustType(input.Enter)286287// Press key combinations288page.Keyboard.MustPress(input.ControlLeft)289page.Keyboard.MustType(input.KeyA)290page.Keyboard.MustRelease(input.ControlLeft)291292// Mouse click at coordinates293page.Mouse.MustClick(input.MouseLeft)294page.Mouse.MustMoveTo(100, 200)295```296297### 4. Network Request Interception (Hijacking)298299```go300router := browser.HijackRequests()301defer router.MustStop()302303// Block all image requests304router.MustAdd("*.png", func(ctx *rod.Hijack) {305 ctx.Response.Fail(proto.NetworkErrorReasonBlockedByClient)306})307308// Modify request headers309router.MustAdd("*api.example.com*", func(ctx *rod.Hijack) {310 ctx.Request.Req().Header.Set("Authorization", "Bearer token123")311 ctx.MustLoadResponse()312})313314// Modify response body315router.MustAdd("*.js", func(ctx *rod.Hijack) {316 ctx.MustLoadResponse()317 ctx.Response.SetBody(ctx.Response.Body() + "\n// injected")318})319320go router.Run()321```322323### 5. Waiting Strategies324325```go326// Wait for page load event327page.MustWaitLoad()328329// Wait for no pending network requests (AJAX idle)330wait := page.MustWaitRequestIdle()331page.MustElement("#search").MustInput("query")332wait()333334// Wait for element to be stable (not animating)335page.MustElement(".modal").MustWaitStable().MustClick()336337// Wait for element to become invisible338page.MustElement(".loading").MustWaitInvisible()339340// Wait for JavaScript condition341page.MustWait(`() => document.title === 'Ready'`)342343// Wait for specific navigation/event344wait := page.WaitEvent(&proto.PageLoadEventFired{})345page.MustNavigate("https://example.com")346wait()347```348349### 6. Race Selectors (Multiple Outcomes)350351Handle pages where the result can be one of several outcomes (e.g., login success vs error):352353```go354page.MustElement("#username").MustInput("user")355page.MustElement("#password").MustInput("pass").MustType(input.Enter)356357// Race between success and error selectors358elm := page.Race().359 Element(".dashboard").MustHandle(func(e *rod.Element) {360 fmt.Println("Login successful:", e.MustText())361 }).362 Element(".error-message").MustDo()363364if elm.MustMatches(".error-message") {365 log.Fatal("Login failed:", elm.MustText())366}367```368369### 7. Screenshots & PDF370371```go372// Full-page screenshot373page.MustScreenshot("page.png")374375// Custom screenshot (JPEG, specific region)376img, _ := page.Screenshot(true, &proto.PageCaptureScreenshot{377 Format: proto.PageCaptureScreenshotFormatJpeg,378 Quality: gson.Int(90),379 Clip: &proto.PageViewport{380 X: 0, Y: 0, Width: 1280, Height: 800, Scale: 1,381 },382})383utils.OutputFile("screenshot.jpg", img)384385// Scroll screenshot (captures full scrollable page)386img, _ := page.MustWaitStable().ScrollScreenshot(nil)387utils.OutputFile("full_page.jpg", img)388389// PDF export390page.MustPDF("output.pdf")391```392393### 8. Concurrent Page Pool394395```go396pool := rod.NewPagePool(5) // max 5 concurrent pages397398create := func() *rod.Page {399 return browser.MustIncognito().MustPage()400}401402var wg sync.WaitGroup403for _, url := range urls {404 wg.Add(1)405 go func(u string) {406 defer wg.Done()407408 page := pool.MustGet(create)409 defer pool.Put(page)410411 page.MustNavigate(u).MustWaitLoad()412 fmt.Println(page.MustInfo().Title)413 }(url)414}415wg.Wait()416417pool.Cleanup(func(p *rod.Page) { p.MustClose() })418```419420### 9. Event Handling421422```go423// Listen for console.log output424go page.EachEvent(func(e *proto.RuntimeConsoleAPICalled) {425 if e.Type == proto.RuntimeConsoleAPICalledTypeLog {426 fmt.Println(page.MustObjectsToJSON(e.Args))427 }428})()429430// Wait for a specific event before proceeding431wait := page.WaitEvent(&proto.PageLoadEventFired{})432page.MustNavigate("https://example.com")433wait()434```435436### 10. File Download437438```go439wait := browser.MustWaitDownload()440441page.MustElementR("a", "Download PDF").MustClick()442443data := wait()444utils.OutputFile("downloaded.pdf", data)445```446447### 11. JavaScript Evaluation448449```go450// Execute JS on the page451page.MustEval(`() => console.log("hello")`)452453// Pass parameters and get return value454result := page.MustEval(`(a, b) => a + b`, 1, 2)455fmt.Println(result.Int()) // 3456457// Eval on a specific element ("this" = the DOM element)458title := page.MustElement("title").MustEval(`() => this.innerText`).String()459460// Direct CDP calls for features Rod doesn't wrap461proto.PageSetAdBlockingEnabled{Enabled: true}.Call(page)462```463464### 12. Loading Chrome Extensions465466```go467extPath, _ := filepath.Abs("./my-extension")468469u := launcher.New().470 Set("load-extension", extPath).471 Headless(false). // extensions require headed mode472 MustLaunch()473474browser := rod.New().ControlURL(u).MustConnect()475```476477---478479## Examples480481See the `examples/` directory for complete, runnable Go files:482- `examples/basic_scrape.go` โ Minimal scraping example483- `examples/stealth_page.go` โ Anti-detection with go-rod/stealth484- `examples/request_hijacking.go` โ Intercepting and modifying network requests485- `examples/concurrent_pages.go` โ Page pool for concurrent scraping486487---488489## Best Practices490491- โ **ALWAYS use `stealth.MustPage(browser)`** instead of `browser.MustPage()` for real-world sites.492- โ **ALWAYS `defer browser.MustClose()`** immediately after connecting.493- โ Use the error-returning API (not `Must*`) in production code.494- โ Set explicit timeouts with `.Timeout()` โ never rely on defaults for production.495- โ Use `browser.MustIncognito().MustPage()` for isolated sessions.496- โ Use `PagePool` for concurrent scraping instead of spawning unlimited pages.497- โ Use `MustWaitStable()` before clicking elements that might be animating.498- โ Use `MustWaitRequestIdle()` after actions that trigger AJAX calls.499- โ Use `launcher.New().Headless(false).Devtools(true)` for debugging.500- โ **NEVER** use `time.Sleep()` for waiting โ use Rod's built-in wait methods.501- โ **NEVER** create a new `Browser` per task โ create one Browser, use multiple `Page` instances.502- โ **NEVER** use `browser.MustPage()` for production scraping โ use `stealth.MustPage()`.503- โ **NEVER** ignore errors in production โ always handle them explicitly.504- โ **NEVER** forget to defer-close browsers, pages, and hijack routers.505506## Common Pitfalls507508- **Problem:** Element not found even though it exists on the page.509 **Solution:** The element may be inside an iframe or shadow DOM. Use `page.MustSearch()` instead of `page.MustElement()` โ it searches across all iframes and shadow DOMs.510511- **Problem:** Click doesn't work because the element is animating.512 **Solution:** Call `el.MustWaitStable()` before `el.MustClick()`.513514- **Problem:** Bot detection despite using stealth.515 **Solution:** Combine `stealth.MustPage()` with: randomized viewport sizes, realistic User-Agent strings, human-like input delays between keystrokes, and random idle behaviors (scroll, hover).516517- **Problem:** Browser process leaks (zombie processes).518 **Solution:** Always `defer browser.MustClose()`. Rod uses [leakless](https://github.com/ysmood/leakless) to kill zombies after main process crash, but explicit cleanup is preferred.519520- **Problem:** Timeout errors on slow pages.521 **Solution:** Use chained context: `page.Timeout(30 * time.Second).MustWaitLoad()`. For AJAX-heavy pages, use `MustWaitRequestIdle()` instead of `MustWaitLoad()`.522523- **Problem:** HijackRequests router not intercepting requests.524 **Solution:** You must call `go router.Run()` after setting up routes, and `defer router.MustStop()` for cleanup.525526## Limitations527528- **CAPTCHAs:** Rod does not include CAPTCHA solving. External services (2captcha, etc.) must be integrated separately.529- **Extreme Anti-Bot:** While `go-rod/stealth` handles common detection (WebDriver, plugin fingerprints, WebGL), extremely strict systems (some Cloudflare configurations, Akamai Bot Manager) may still detect automation. Additional measures (residential proxies, human-like behavioral patterns) may be needed.530- **DRM Content:** Cannot interact with DRM-protected media (e.g., Widevine).531- **Resource Usage:** Each browser instance consumes significant RAM (~100-300MB+). Use `PagePool` and limit concurrency on memory-constrained systems.532- **Extensions in Headless:** Chrome extensions do not work in headless mode. Use `Headless(false)` with XVFB for server environments.533- **Platform:** Requires a Chromium-compatible browser. Does not support Firefox or Safari.534535## Documentation References536537- [Official Documentation](https://go-rod.github.io/) โ Guides, tutorials, FAQ538- [Go API Reference](https://pkg.go.dev/github.com/go-rod/rod) โ Complete type and method documentation539- [go-rod/stealth](https://github.com/go-rod/stealth) โ Anti-bot detection plugin540- [Examples (source)](https://github.com/go-rod/rod/blob/main/examples_test.go) โ Official example tests541- [Rod vs Chromedp Comparison](https://github.com/nichochar/go-rod.github.io/blob/main/lib/examples/compare-chromedp) โ Migration reference542- [Chrome DevTools Protocol Docs](https://chromedevtools.github.io/devtools-protocol/) โ Underlying protocol reference543- [Chrome CLI Flags Reference](https://peter.sh/experiments/chromium-command-line-switches) โ Launcher flag documentation544- `references/api-reference.md` โ Quick-reference cheat sheet545
Full transparency โ inspect the skill content before installing.