FlareSolverr Go Edition

June 1, 2026 · View on GitHub

A high-performance Cloudflare bypass proxy server written in Go. This is a drop-in replacement for the original Python FlareSolverr with significant performance improvements.

Drop-in Replacement

This project is fully API-compatible with the original FlareSolverr. You can replace your existing FlareSolverr instance without changing any client code:

  • Same API endpoints (/ and /v1)
  • Same request/response format
  • Same command names (request.get, request.post, sessions.create, etc.) plus new sessions.keepalive
  • Same default port (8191)

Just swap the Docker image or binary and you're done.

Features

  • Browser Pooling - Reuses browser instances instead of spawning new ones per request (150-250MB vs 400-700MB)
  • Direct CDP Protocol - Uses Chrome DevTools Protocol directly, bypassing Selenium overhead
  • Go Concurrency - Native goroutines for better concurrency than Python's GIL
  • Memory Management - Active memory monitoring with automatic browser recycling
  • Session Support - TTL-based session management with per-request TTL override, keepalive command, and automatic cleanup
  • Cloudflare Bypass - Solves JavaScript challenges and Turnstile CAPTCHAs
  • Anti-Fingerprinting - 20 composable stealth patches with configurable fingerprint profiles (default, Windows, macOS, minimal)
  • Per-Session Chrome Flags - Custom window size, language, timezone, and Chrome flags per session with dedicated browser instances
  • Human-Like Behavior - Bezier curve mouse movements, randomized timing, and natural scroll patterns
  • Two-Phase CDP Bypass - Bypasses Cloudflare's managed challenge loop by launching a clean Chrome without CDP for challenge resolution
  • External CAPTCHA Fallback - Pluggable provider registry with 2Captcha, CapSolver, and anti-captcha.com for Turnstile and hCaptcha
  • hCaptcha Support - Detects hCaptcha challenges, extracts sitekeys, and solves via external providers with token injection
  • Hot-Reload Selectors - Update challenge selectors via file watching or remote URL without restarts
  • Adaptive Solving - Per-domain tracking of which solving methods work best
  • Custom JS Execution - Run arbitrary JavaScript on pages after challenge solving
  • Binary Downloads - Download files as base64 through the browser (bypasses TLS fingerprinting)
  • Prometheus Metrics - /metrics endpoint compatible with Prometheus/Grafana
  • OpenAPI Docs - /docs endpoint serves the full API specification
  • CLI Dashboard - Optional split-screen TUI showing live requests and server stats (DASHBOARD_ENABLED=true)
  • Multi-Architecture - Docker images for amd64, arm64, and armv7
  • Docker Support - Production-ready Docker image with Xvfb

Quick Start

docker run -d -p 8191:8191 --name flaresolverr rorqualx/flaresolverr-go:latest

The image declares /tmp/rod as a VOLUME, so Chromium's per-browser user-data dirs land in an anonymous Docker volume (cleaned up by docker rm -v) instead of the container's writable layer. These dirs are removed after every browser close, so they do not accumulate on disk.

⚠️ Do not mount a tmpfs at /tmp/rod (or /tmp). It is a common suggestion for "putting browser scratch space in RAM", but it breaks Chromium launch (GitHub issue #10):

  • tmpfs defaults to noexec, so Chromium's launch helper cannot exec → permission denied.
  • A nested tmpfs at /tmp/rod breaks Chrome's process-singleton lock → Failed to get the debug url: Opening in existing browser session. This is the failure most commonly hit on Unraid.

The container works correctly with no tmpfs flag — just use the default anonymous volume. If you really want browser scratch space in RAM, mount a tmpfs with the exec option at a path that is not nested under /tmp/rod.

From Source

go build -o flaresolverr ./cmd/flaresolverr
./flaresolverr

Check Version

./flaresolverr --version

API Reference

The API accepts POST requests with JSON body at both / and /v1 endpoints. All commands return the same response format.

Endpoints

EndpointMethodDescription
/POSTMain API endpoint (legacy)
/v1POSTMain API endpoint (recommended)
/healthGETHealth check with pool and domain stats
/metricsGETPrometheus-compatible metrics
/docsGETOpenAPI 3.0 specification (YAML)

Commands

request.get - Fetch a URL

Navigates to a URL, solves any Cloudflare challenges, and returns the page content.

curl -X POST http://localhost:8191/v1 \
  -H "Content-Type: application/json" \
  -d '{
    "cmd": "request.get",
    "url": "https://example.com",
    "maxTimeout": 60000
  }'

request.post - Submit a POST request

Navigates to a URL with POST data.

curl -X POST http://localhost:8191/v1 \
  -H "Content-Type: application/json" \
  -d '{
    "cmd": "request.post",
    "url": "https://example.com/login",
    "postData": "username=user&password=pass",
    "maxTimeout": 60000
  }'

sessions.create - Create a persistent session

Creates a session that persists cookies and browser state across requests.

curl -X POST http://localhost:8191/v1 \
  -H "Content-Type: application/json" \
  -d '{
    "cmd": "sessions.create",
    "session": "my-session-id",
    "session_ttl_minutes": 60
  }'

The optional session_ttl_minutes parameter overrides the global SESSION_TTL for this session (1-1440 minutes). If omitted, the server default is used.

sessions.list - List active sessions

curl -X POST http://localhost:8191/v1 \
  -H "Content-Type: application/json" \
  -d '{
    "cmd": "sessions.list"
  }'

sessions.keepalive - Refresh a session's TTL

Touches the session to prevent expiration. Optionally extends the TTL.

curl -X POST http://localhost:8191/v1 \
  -H "Content-Type: application/json" \
  -d '{
    "cmd": "sessions.keepalive",
    "session": "my-session-id",
    "keepaliveTtl": 120
  }'

If keepaliveTtl is provided (in minutes), the session's TTL is updated to that value. If omitted, the session is simply touched to reset its inactivity timer.

sessions.destroy - Destroy a session

curl -X POST http://localhost:8191/v1 \
  -H "Content-Type: application/json" \
  -d '{
    "cmd": "sessions.destroy",
    "session": "my-session-id"
  }'

Request Parameters

ParameterTypeRequiredDescription
cmdstringYesCommand to execute
urlstringFor request.*Target URL to navigate to
sessionstringNoSession ID for persistent sessions
session_ttl_minutesintNoPer-session TTL override in minutes (1-1440, default: server SESSION_TTL)
maxTimeoutintNoMaximum timeout in milliseconds (default: 60000)
cookiesarrayNoCookies to set before navigation
proxyobjectNoProxy configuration for this request
postDatastringFor request.postURL-encoded POST data
returnOnlyCookiesboolNoReturn only cookies, not HTML
returnScreenshotboolNoReturn base64 PNG screenshot
disableMediaboolNoBlock images, CSS, fonts to speed up loading
waitInSecondsintNoWait N seconds before returning response
contentTypestringNoPOST content type: application/json or application/x-www-form-urlencoded
headersobjectNoCustom HTTP headers (max 50)
tabsTillVerifyintNoTab presses for Turnstile keyboard navigation (0-50)
downloadboolNoDownload URL as binary, return base64 in response field
followRedirectsboolNoFollow HTTP redirects (default: true)
userAgentstringNoOverride User-Agent for this request
returnRawHtmlboolNoReturn raw HTML before JavaScript renders
executeJsstringNoCustom JavaScript to execute after solving
captchaSolverstringNoPer-request captcha provider: 2captcha, capsolver, anticaptcha, or none
captchaApiKeystringNoPer-request captcha API key
keepaliveTtlintNoNew TTL in minutes for sessions.keepalive (0 = just touch, max 1440)
cookieExtractDelayintNoSeconds to wait before extracting cookies (0-30). Captures late-set JS cookies
browserFlagsobjectNoPer-session Chrome flag overrides (sessions.create only). See below
fingerprintobjectNoPer-request browser fingerprint customization. See below
{
  "name": "session_id",
  "value": "abc123",
  "domain": ".example.com",
  "path": "/",
  "secure": true,
  "httpOnly": true
}

Proxy Object

{
  "url": "http://proxy.example.com:8080",
  "username": "user",
  "password": "pass"
}

Supported proxy schemes: http, https, socks4, socks5

Browser Flags Object

Per-session Chrome flag overrides. Sessions created with custom flags get a dedicated browser (not from the pool) that is closed when the session is destroyed.

{
  "windowSize": "1280,720",
  "language": "fr-FR",
  "timezone": "Europe/Paris",
  "headless": true,
  "disableGpu": false,
  "extraArgs": ["--disable-extensions"]
}
FieldTypeDescription
windowSizestringWindow dimensions as "width,height" (e.g. "1280,720")
languagestringAccept-Language override (e.g. "fr-FR", "de-DE")
timezonestringTimezone for stealth patches (e.g. "Europe/Paris")
headlessboolOverride global headless setting
disableGpuboolForce software rendering
extraArgsarrayAdditional Chrome flags (validated against security whitelist)

Security: extraArgs are validated against an allowed whitelist. Dangerous flags like --disable-web-security and --remote-debugging-port are blocked.

Example: Create a French-language session with a smaller viewport:

curl -X POST http://localhost:8191/v1 \
  -H "Content-Type: application/json" \
  -d '{
    "cmd": "sessions.create",
    "session": "my-french-session-01",
    "browserFlags": {
      "windowSize": "1280,720",
      "language": "fr-FR",
      "timezone": "Europe/Paris"
    }
  }'

Fingerprint Object

Per-request browser fingerprint customization. Controls which stealth patches apply and what values they report.

{
  "profile": "desktop-chrome-windows",
  "overrides": {
    "timezone": "Asia/Tokyo",
    "screenWidth": 1920,
    "screenHeight": 1080
  },
  "disablePatches": ["canvas", "audio"]
}
FieldTypeDescription
profilestringBuiltin profile name (see below)
overridesobjectOverride individual fingerprint dimensions
disablePatchesarrayStealth patches to skip by name

Builtin Profiles:

ProfileDescription
defaultAll patches enabled, standard 1920x1080, 8GB memory, 4 cores
desktop-chrome-windowsWindows UA, NVIDIA WebGL, 16GB memory, 8 cores
desktop-chrome-macmacOS UA, Apple M1 WebGL, 2560x1440, 16GB, 10 cores
minimalOnly essential patches (webdriver, plugins, chrome-runtime)

Override Keys: timezone, locale, screenWidth, screenHeight, deviceMemory, hardwareConcurrency, webglVendor, webglRenderer, canvasNoiseSeed

Available Patches: webrtc, webdriver, plugins, languages, chrome-runtime, permissions, connection, hardware-concurrency, device-memory, tostring, webgl, notifications, canvas, audio, battery, speech, fonts, timezone, screen-position, device-pixel-ratio

Example: Request with Windows fingerprint and Tokyo timezone:

curl -X POST http://localhost:8191/v1 \
  -H "Content-Type: application/json" \
  -d '{
    "cmd": "request.get",
    "url": "https://example.com",
    "maxTimeout": 60000,
    "fingerprint": {
      "profile": "desktop-chrome-windows",
      "overrides": {"timezone": "Asia/Tokyo"}
    }
  }'

Response Format

{
  "status": "ok",
  "message": "Challenge solved successfully",
  "startTimestamp": 1704067200000,
  "endTimestamp": 1704067205000,
  "version": "1.0.0",
  "solution": {
    "url": "https://example.com/",
    "status": 200,
    "response": "<html>...</html>",
    "cookies": [
      {
        "name": "cf_clearance",
        "value": "...",
        "domain": ".example.com",
        "path": "/",
        "expires": 1704153600,
        "httpOnly": true,
        "secure": true,
        "sameSite": "None"
      }
    ],
    "userAgent": "Mozilla/5.0...",
    "screenshot": "base64...",
    "turnstile_token": "..."
  }
}

Response Fields

FieldTypeDescription
statusstring"ok" or "error"
messagestringHuman-readable status message
startTimestampintRequest start time (Unix ms)
endTimestampintRequest end time (Unix ms)
versionstringFlareSolverr version
solutionobjectSolution data (on success)
sessionsarrayList of session IDs (for sessions.list)

Solution Fields

FieldTypeDescription
urlstringFinal URL after redirects
statusintHTTP status code
responsestringPage HTML content
cookiesarrayAll cookies from the page
userAgentstringBrowser user agent
screenshotstringBase64 PNG (if requested)
turnstile_tokenstringCloudflare Turnstile token (if present)
localStorageobjectAll localStorage key-value pairs (for debugging)
sessionStorageobjectAll sessionStorage key-value pairs (for debugging)
responseHeadersobjectExtracted response metadata (cf-ray, etc.)
responseTruncatedbooltrue if HTML was truncated due to 10MB size limit (optional)
cookieErrorstringError message if cookies could not be retrieved (optional)
responseEncodingstring"base64" when download=true (optional)
executeJsResultstringResult of executeJs custom JavaScript (optional)
browserVersionstringChrome major version for TLS profile matching (optional)
rateLimitedbooltrue if rate limiting detected (optional)
suggestedDelayMsintRecommended delay before retry in ms (optional)
errorCodestringSpecific error code like CF_1015 (optional)
errorCategorystringError category: rate_limit, access_denied, captcha, geo_blocked (optional)

Rate Limit Detection

When the target site returns a rate limiting or access denied response, additional fields are included in the solution:

{
  "solution": {
    "url": "https://example.com/",
    "status": 403,
    "response": "<html>Access denied...</html>",
    "rateLimited": true,
    "suggestedDelayMs": 5000,
    "errorCode": "ACCESS_DENIED",
    "errorCategory": "access_denied"
  }
}

Supported Error Codes:

CodeCategoryDescriptionSuggested Delay
CF_1015rate_limitCloudflare rate limit60s
CF_1020access_deniedCloudflare suspicious request30s
CF_1006-1008access_deniedCloudflare access denied30s
CF_1009geo_blockedCloudflare geo-restrictionN/A
CF_1010access_deniedBrowser signature rejected30s
ACCESS_DENIEDaccess_deniedGeneric access denied5s
RATE_LIMITEDrate_limitGeneric rate limit10s
TOO_MANY_REQUESTSrate_limitToo many requests10s
HTTP_429rate_limitHTTP 429 status60s
HTTP_503rate_limitService unavailable30s
CAPTCHA_REQUIREDcaptchaCAPTCHA challengeN/A

Response Headers

The following headers are included in responses for domain-level statistics:

HeaderDescription
X-Domain-Suggested-DelayRecommended delay in ms based on domain history
X-Domain-Error-RateError rate (0.0-1.0) for this domain
X-Domain-Request-CountTotal requests tracked for this domain

Configuration

All configuration is done via environment variables.

Server Settings

VariableDefaultDescription
HOST0.0.0.0Server bind address
PORT8191Server port

Browser Settings

VariableDefaultDescription
HEADLESStrueRun browser in headless mode
BROWSER_PATH(auto)Path to Chrome/Chromium executable
BROWSER_POOL_SIZE3Number of browser instances in pool
BROWSER_POOL_TIMEOUT30sTimeout for acquiring a browser
MAX_MEMORY_MB2048Memory limit before recycling browsers

Session Settings

VariableDefaultDescription
SESSION_TTL30mSession time-to-live
SESSION_CLEANUP_INTERVAL1mCleanup interval for expired sessions
MAX_SESSIONS100Maximum concurrent sessions

Timeout Settings

VariableDefaultDescription
DEFAULT_TIMEOUT60sDefault request timeout
MAX_TIMEOUT300sMaximum allowed timeout

Proxy Settings

VariableDefaultDescription
PROXY_URL(none)Default proxy URL for all requests
PROXY_USERNAME(none)Default proxy username
PROXY_PASSWORD(none)Default proxy password

Security Settings

VariableDefaultDescription
RATE_LIMIT_ENABLEDtrueEnable rate limiting
RATE_LIMIT_RPM60Requests per minute per IP
TRUST_PROXYfalseTrust X-Forwarded-For headers
CORS_ALLOWED_ORIGINS(all)Comma-separated allowed origins
ALLOW_LOCAL_PROXIEStrueAllow localhost/private IP proxies
IGNORE_CERT_ERRORSfalseIgnore TLS certificate errors
DNS_REBINDING_PROTECTIONtruePin response URL to the request-time IP. Set false for sites serving identical content across multiple TLDs/CDN IPs (SSRF protection stays on)
API_KEY_ENABLEDfalseEnable API key authentication
API_KEY(none)Required API key (use 16+ chars)

API Key Authentication

When enabled, all requests (except /health) require authentication:

# Via header (recommended)
curl -X POST http://localhost:8191/v1 \
  -H "X-API-Key: your-secret-key" \
  -H "Content-Type: application/json" \
  -d '{"cmd": "sessions.list"}'

# Via query parameter
curl -X POST "http://localhost:8191/v1?api_key=your-secret-key" \
  -H "Content-Type: application/json" \
  -d '{"cmd": "sessions.list"}'

Enable in Docker:

environment:
  - API_KEY_ENABLED=true
  - API_KEY=your-secret-key-at-least-16-chars

CAPTCHA Solver Settings

External CAPTCHA solver fallback for Turnstile and hCaptcha challenges that native solving cannot handle.

VariableDefaultDescription
CAPTCHA_NATIVE_ATTEMPTS3Native solve attempts before external fallback (1-10)
CAPTCHA_FALLBACK_ENABLEDfalseEnable external CAPTCHA solver fallback
TWOCAPTCHA_API_KEY(none)2Captcha API key
CAPSOLVER_API_KEY(none)CapSolver API key
ANTICAPTCHA_API_KEY(none)anti-captcha.com API key
CAPTCHA_PRIMARY_PROVIDER2captchaPrimary provider: 2captcha, capsolver, or anticaptcha
CAPTCHA_SOLVER_TIMEOUT120sTimeout for external solver API (30s-300s)

Supported CAPTCHA types:

  • Turnstile — Cloudflare's challenge widget (native + external solving)
  • hCaptcha — Detected automatically, solved via external provider

How it works:

  1. FlareSolverr attempts native Turnstile solving first (click methods, keyboard, etc.)
  2. If native solving fails after CAPTCHA_NATIVE_ATTEMPTS, it falls back to the external solver
  3. For hCaptcha, external solving is used directly (no native solving available)
  4. External solver extracts the sitekey, submits to the provider, and injects the token
  5. Per-request override: use captchaSolver and captchaApiKey fields in the request

Example configuration:

environment:
  - CAPTCHA_FALLBACK_ENABLED=true
  - TWOCAPTCHA_API_KEY=your-2captcha-api-key
  - CAPTCHA_NATIVE_ATTEMPTS=2

Selectors Settings

Hot-reload capable selectors for adapting to Cloudflare changes without restarts. Supports local files and remote URLs.

VariableDefaultDescription
SELECTORS_PATH(none)Path to external selectors.yaml override file
SELECTORS_HOT_RELOADfalseEnable file watching for automatic reload
SELECTORS_REMOTE_URL(none)HTTP(S) URL to fetch selectors from
SELECTORS_REMOTE_REFRESH1hRefresh interval for remote selectors (5m-24h)

When SELECTORS_HOT_RELOAD is enabled, changes to the selectors file are automatically detected and applied without restarting the service.

When SELECTORS_REMOTE_URL is configured, selectors are fetched periodically from the remote URL. File selectors take priority over remote selectors if both are configured.

Logging & Monitoring

VariableDefaultDescription
LOG_LEVELinfoLog level (debug, info, warn, error)
LOG_HTMLfalseLog HTML responses (verbose)
LOG_FILE(none)Path to log file (in addition to stdout)
TZ(none)Browser timezone (e.g., America/New_York)
LANG(none)Browser language (e.g., en_GB)
TEST_URLhttps://www.google.comURL to verify browser works on startup
DASHBOARD_ENABLEDtrueTUI dashboard (auto-disables without TTY)
PPROF_ENABLEDfalseEnable pprof profiling
PPROF_PORT6060pprof server port
PPROF_BIND_ADDR127.0.0.1pprof bind address

CLI Dashboard

FlareSolverr launches a split-screen terminal dashboard by default when running in an interactive terminal:

┌──────── Incoming Requests ────────┬──────────── Server Stats ──────────┐
│ 16:03:22 POST /v1 example.com 200 │ Server                             │
│ 16:03:21 POST /v1 site.org   200  │  Uptime: 2h 14m  Req/s: 2.1       │
│ 16:03:19 POST /v1 example.com 200 │  Total: 1,247    Goroutines: 42   │
│ 16:03:15 POST /v1 blocked.io 403  │  Memory: 312 MB                   │
│                                    │ Pool                               │
│                                    │  2/3 available                     │
│                                    │ Sessions                           │
│                                    │  4 active                          │
│                                    │ Domains (3)                        │
│                                    │  example.com  842 req  96% ok     │
│                                    │  site.org     231 req  89% ok     │
└────────────────────────────────────┴────────────────────────────────────┘

Left pane: Live request log with method, path, status code, and latency. Right pane: Server stats including uptime, request rate, memory, browser pool state, active sessions, and top domains by request count.

Press q or ctrl+c to exit. Logging is suppressed while the dashboard is active.

The dashboard auto-disables when stdout is not a terminal (e.g., Docker logs, piped output). To explicitly disable it: DASHBOARD_ENABLED=false.

Docker Compose Example

version: "3.8"
services:
  flaresolverr:
    image: rorqualx/flaresolverr-go:latest
    container_name: flaresolverr
    ports:
      - "8191:8191"
    environment:
      - LOG_LEVEL=info
      - BROWSER_POOL_SIZE=3
      - MAX_TIMEOUT=300s
    restart: unless-stopped

Migration from Python FlareSolverr

  1. Stop your existing FlareSolverr container
  2. Update your docker-compose.yml or run command to use this image
  3. Start the new container
  4. Your existing clients will work without any changes

No code changes required in your applications.

There is one minor difference in cookie field naming:

FieldPython FlareSolverrGo Edition
Expirationexpiryexpires

Both values are Unix timestamps (seconds since epoch). If your client code explicitly checks for cookie expiration, use this pattern to handle both:

JavaScript/TypeScript:

const expiry = cookie.expiry ?? cookie.expires;

Python:

expiry = cookie.get('expiry') or cookie.get('expires')

Go:

expiry := cookie.Expiry
if expiry == 0 {
    expiry = cookie.Expires
}

The Go edition includes extra fields not in Python FlareSolverr:

FieldTypeDescription
sizeintCookie size in bytes
sessionboolWhether it's a session cookie

These are optional and can be safely ignored if not needed.

Performance Comparison

MetricPython FlareSolverrGo Edition
Memory per session400-700 MB150-250 MB
Startup time5-10s<1s
Request latencyHigher (Selenium)Lower (CDP)
Concurrent requestsLimited (GIL)Native goroutines

Health Check

curl http://localhost:8191/health

Returns:

{
  "status": "ok",
  "message": "FlareSolverr is ready",
  "version": "1.0.0",
  "pool": {
    "size": 3,
    "available": 2,
    "acquired": 150,
    "released": 148,
    "recycled": 5,
    "errors": 2
  },
  "domainStats": {
    "example.com": {
      "requestCount": 45,
      "successCount": 42,
      "errorCount": 3,
      "rateLimitCount": 2,
      "avgLatencyMs": 2340,
      "lastRequestTime": "2025-01-15T10:35:00Z",
      "lastRateLimited": "2025-01-15T10:30:00Z",
      "suggestedDelayMs": 5000
    }
  },
  "defaults": {
    "minDelayMs": 1000,
    "maxDelayMs": 30000
  }
}

Pool Statistics

FieldDescription
sizeConfigured pool size (number of browser instances)
availableBrowsers currently idle and ready for requests
acquiredTotal browsers acquired from pool
releasedTotal browsers returned to pool
recycledBrowsers recycled due to memory or errors
errorsTotal browser operation errors

Domain Statistics

When requests have been made, the health endpoint includes per-domain statistics:

FieldDescription
requestCountTotal requests to this domain
successCountSuccessful requests (2xx/3xx, no rate limiting)
errorCountFailed requests
rateLimitCountRequests that were rate limited
avgLatencyMsAverage response time
lastRateLimitedTimestamp of last rate limit event
suggestedDelayMsRecommended delay between requests

The suggestedDelayMs is calculated using an algorithm inspired by Scrapy's AutoThrottle, considering latency, error rates, and recent rate limiting events.

Performance Tuning

Understanding Concurrency

FlareSolverr uses a browser pool to handle concurrent requests efficiently:

  • BROWSER_POOL_SIZE controls how many requests can be processed simultaneously
  • RATE_LIMIT_RPM controls the maximum requests per minute per IP

Example behavior with defaults (BROWSER_POOL_SIZE=3, RATE_LIMIT_RPM=60):

  • 3 requests can be processed in parallel
  • Additional requests queue until a browser becomes available
  • Rate limiting kicks in at 60 requests/minute per client IP

Tuning for Your Use Case

ScenarioRecommended Settings
Low volume, fast responseBROWSER_POOL_SIZE=2, RATE_LIMIT_RPM=30
Medium volumeBROWSER_POOL_SIZE=3, RATE_LIMIT_RPM=60 (defaults)
High volume, more memoryBROWSER_POOL_SIZE=5, RATE_LIMIT_RPM=120
Single client, max throughputBROWSER_POOL_SIZE=5, RATE_LIMIT_ENABLED=false

Memory Considerations

Each browser instance consumes approximately:

  • 100-150MB base memory
  • +50-100MB per active page (during request processing)

For a pool size of 3 with 5 active pages, expect 500-700MB total memory usage.

Use MAX_MEMORY_MB to set a memory ceiling. When exceeded, browsers are automatically recycled.

Troubleshooting

Common Issues

"Access denied" errors

  • IP blocked: The target site may have blocked your IP. Try using a proxy.
  • Bot detection: Some sites have aggressive bot detection. Session reuse can help maintain cookies.
  • Rate limiting: Reduce request frequency or use RATE_LIMIT_RPM to self-throttle.

Session requests failing

  • Sessions auto-expire after SESSION_TTL (default: 30 minutes)
  • Use sessions.keepalive to refresh a session's TTL without making a full request
  • Use keepaliveTtl parameter to extend the TTL (e.g., "keepaliveTtl": 120 for 2 hours)
  • Always check if session exists with sessions.list before using
  • Destroy and recreate sessions if they become stale

High memory usage

  • Reduce BROWSER_POOL_SIZE (each browser uses 100-150MB)
  • Enable disableMedia: true in requests to skip images/CSS
  • Set MAX_MEMORY_MB to trigger automatic browser recycling

Browser pool exhaustion

  • Increase BROWSER_POOL_SIZE for higher concurrency
  • Increase BROWSER_POOL_TIMEOUT if requests timeout waiting for browsers
  • Check /health endpoint to monitor pool stats

Container crashes

  • Ensure adequate memory (minimum 512MB recommended)
  • Check Docker logs for OOM kills
  • Reduce BROWSER_POOL_SIZE if memory constrained

Debug Mode

Enable debug logging to see detailed information:

docker run -e LOG_LEVEL=debug -p 8191:8191 rorqualx/flaresolverr-go:latest

Getting Help

  • GitHub Issues - Bug reports and feature requests
  • Check /health endpoint for pool statistics
  • Review container logs for error messages

Changelog

See CHANGELOG.md for version history and release notes.

Attribution

Built with Claude Code by Anthropic

License

MIT License - See LICENSE for details.