Solverr is a proxy server to bypass Cloudflare and DDoS-GUARD protection. It fuses the two best open-source solvers into one service and switches between them automatically, so you get reliable solving and coverage of the newer challenge tiers.
- Chrome engine (default) — the original FlareSolverr approach: Selenium + undetected-chromedriver driving a real Chromium. Fast, session-capable, and clears most sites.
- Stealth engine — Byparr's stack: Camoufox (an anti-detect Firefox that patches its fingerprint in compiled code) + playwright-captcha. Clears the newer Cloudflare Turnstile / Managed Challenges that headless Chromium gives up on.
It speaks the exact FlareSolverr /v1 API on port 8191, so it is a drop-in replacement: existing clients (the *arr stack, manga/novel readers, etc.) work unchanged.
Beyond the two engines, it keeps sessions warm so repeat requests to a host skip the challenge, and adds an optional passthrough proxy for clients (indexer managers and the like) that re-fetch the URL themselves.
- Getting started — How it works · Quick start · Installation
- Using it — Engines & fallback · Sessions & cleanup · API usage · Passthrough proxy
- Reference — Configuration · Proxy & reliability · Prometheus exporter · Troubleshooting
Solverr waits for requests in an idle state. When one arrives it opens the URL in a real browser, waits until the challenge is solved (or the timeout is hit), and returns the page HTML plus the cookies. Those cookies (e.g. cf_clearance, __ddg2_) can then be reused by any HTTP client to reach the site directly.
Each request picks an engine and automatically falls back to the other when the first is blocked, times out, or hands back an unsolved "Just a moment..." page. Solverr remembers which engine cleared each host and routes there first next time.
The escalation ladder for a normal request is:
Chrome engine → Camoufox click-solve → (optional) paid CAPTCHA API
Web browsers use a lot of memory. Each session keeps a browser alive; sessionless requests launch one per request. On a low-RAM machine, avoid many concurrent requests. Solverr closes idle sessions automatically (see Sessions & cleanup).
Run the published image (no build) and send your first request. Save this as docker-compose.yml:
services:
solverr:
image: ghcr.io/unseensnick/solverr:latest
container_name: solverr
ports:
- "8191:8191"
shm_size: 512mb
restart: unless-stopped
# See Configuration for the full environment list.docker compose up -d
curl -sX POST 'http://localhost:8191/v1' \
-H 'Content-Type: application/json' \
--data '{ "cmd": "request.get", "url": "https://www.google.com/", "maxTimeout": 60000 }'The response contains the solved page HTML and cookies. See Configuration to tune engines, sessions, proxy, and the passthrough.
The browsers are bundled in the image, so Docker is the easiest path. Quick start runs the published image; the repo's docker-compose.yml is the same setup with the full annotated environment block.
The image reports its own health, so docker ps shows healthy once the API is answering (starting for the first 90 seconds). The check only proves the API is up, not that a solve would succeed, and Docker reports health without acting on it: restart: unless-stopped will not restart a container that is wedged but alive.
Build the image locally instead of pulling it:
docker compose up -d --build # builds the dual-browser image (~2.3 GB)Docker CLI (published image shown; run docker build -t solverr . first and swap the image name to run a local build):
docker run -d \
--name=solverr \
-p 8191:8191 \
--shm-size=512m \
--restart unless-stopped \
ghcr.io/unseensnick/solverr:latestOn a Debian host, make sure libseccomp2 is 2.5.x (sudo apt-cache policy libseccomp2) or the browser may fail to start; update it and restart the Docker daemon.
For development or unsupported architectures. Requires Python 3.9+ (3.11+ recommended for the vendored undetected-chromedriver; the Docker image uses 3.14), and both browsers if you want both engines:
# install Python deps (pip, or `uv pip`)
pip install -r requirements.txt
# Chrome engine: install Chrome or Chromium (+ Xvfb on Linux)
# Stealth engine: install Firefox libraries and fetch Camoufox
playwright install-deps firefox
python -m invisible_playwright fetch
python src/flaresolverr.pySet STEALTH_ENGINE=false to run Chrome-only and skip the Camoufox/Firefox setup entirely.
A client can force an engine per request with the optional engine field; the DEFAULT_ENGINE env var only sets which engine is tried first when a request doesn't specify one.
Request engine |
Behaviour |
|---|---|
omitted / auto |
Start on the engine that last cleared this host (or DEFAULT_ENGINE), then fall back to the other on failure. |
chrome |
Chrome only, no fallback. |
stealth |
Camoufox only, no fallback. |
Fallback triggers when an engine throws (blocked / timeout), or returns a page that still looks like an unsolved challenge. Set ENGINE_FALLBACK=false to disable it. Both attempts share the request's maxTimeout, so falling back never makes the caller wait longer than it asked for; if too little is left for a second browser to launch, Solverr stops and reports why the first engine failed.
DEFAULT_ENGINE=chromedoes not disable fallback. The "no fallback" rows apply only to the per-requestenginefield (a client forcing one engine).DEFAULT_ENGINEjust picks the primary; the other engine is still used as fallback unlessENGINE_FALLBACK=false. Most FlareSolverr clients (the *arr apps, readers) don't send anenginefield, so they always get the fallback path.
A session keeps a browser alive between requests. The cleared cf_clearance cookie stays in that browser's memory, so follow-up requests to the same host skip the challenge and return in 1–3 s instead of re-solving. This is the main reliability and speed lever: solve once, reuse the cookie many times.
Each engine keeps its own session pool under one shared session-id namespace; a session is bound to whichever engine created it (default Chrome). Create one with sessions.create and pass its session id on later requests.
Clients often create a session and never destroy it (a mobile app can be killed before it could). To stop abandoned browsers leaking memory, Solverr runs a background reaper that:
- closes any session idle longer than
SESSION_TTL_MINUTES(default 30). Every request bumps the session's last-used time, so an in-use session is never reaped. - evicts the oldest-idle session once an engine exceeds
SESSION_MAX(default 20).
So sessions.destroy is good practice but optional — cleanup happens automatically.
All requests are POST http://localhost:8191/v1 with a JSON body and Content-Type: application/json.
Python & PowerShell examples
import requests
r = requests.post("http://localhost:8191/v1", json={
"cmd": "request.get", "url": "https://www.google.com/", "maxTimeout": 60000,
})
print(r.text)$body = @{ cmd = "request.get"; url = "https://www.google.com/"; maxTimeout = 60000 } | ConvertTo-Json
irm -UseBasicParsing 'http://localhost:8191/v1' -Headers @{"Content-Type"="application/json"} -Method Post -Body $bodyLaunches a browser that retains cookies until you sessions.destroy it (or the reaper closes it). Reusing the session avoids re-solving challenges and re-launching browsers.
| Parameter | Notes |
|---|---|
| session | Optional. Session id to assign. A random UUID is used if omitted. |
| engine | Optional. chrome (default) or stealth. Binds the session to that engine. |
| proxy | Optional. Eg "proxy": {"url": "http://127.0.0.1:8888"}. Schema required (http://, socks4://, socks5://). Auth supported: {"url": "...", "username": "user", "password": "pass"}. |
Returns the ids of all active sessions across both engines.
{ "status": "ok", "sessions": ["session_id_1", "session_id_2"] }Shuts a session's browser down and frees its resources.
| Parameter | Notes |
|---|---|
| session | The session id to destroy. |
| Parameter | Notes |
|---|---|
| url | Mandatory. |
| engine | Optional. chrome, stealth, or auto (default). See Engines & fallback. |
| session | Optional. Reuse an existing browser instance. Without it, a temporary instance is created and destroyed after the request. |
| session_ttl_minutes | Optional. Recreate the session if it is older than this many minutes. |
| maxTimeout | Optional, default 60000. Max time to answer the request, in milliseconds. It covers the whole request, so a fallback to the other engine shares it rather than starting a fresh one. Clamped to MAX_TIMEOUT_MS (default 180000). |
| cookies | Optional. Cookies to set before loading. Eg "cookies": [{"name": "a", "value": "1"}]. |
| returnOnlyCookies | Optional, default false. Return only cookies; drop response body and headers. |
| returnScreenshot | Optional, default false. Return a Base64 PNG of the final page in the screenshot field. |
| proxy | Optional. Same shape as in sessions.create. Ignored when session is set (use a session proxy instead). |
| waitInSeconds | Optional. Extra seconds to wait after solving, before returning (lets dynamic content load). |
| disableMedia | Optional, default false. Block images, CSS and fonts to speed up navigation. |
| tabs_till_verify | Optional (Chrome engine only). Number of Tab presses to reach a Turnstile checkbox; the resulting token is returned in solution.turnstile_token. Waits up to 5 seconds for a widget that renders after the page loads, so a page with no widget at all costs that long before the request continues without a token. Pressing stops in time to still return a page if the checkbox never yields one. The stealth engine detects Turnstile automatically and does not need this. |
Finding the right
tabs_till_verify. It is the number ofTabpresses from the top of the document to the checkbox, so it depends on how many focusable elements the page puts before the widget. A widget with nothing focusable ahead of it is1. Find yours by sending the same request with1,2,3and so on: the value that comes back with a filledsolution.turnstile_tokenis the one. Use a smallmaxTimeoutwhile you search, because a wrong count spends the whole budget pressing before it gives up.
Reusing cookies? Use the User-Agent Solverr returns (
solution.userAgent) in your own requests. If the UA andcf_clearancedon't match, Cloudflare re-challenges you.
PDFs. When a URL serves a PDF, the stealth engine returns the file itself, Base64-encoded in
solution.response, withsolution.contentTypeset toapplication/pdf. The Chrome engine returns the viewer page instead, so pin"engine": "stealth"when you expect a PDF. Branch oncontentTyperather than assuming: it is absent for ordinary HTML, and also when Solverr could not get the raw bytes and fell back to the viewer page.
Example response (truncated):
{
"status": "ok",
"message": "Challenge solved!",
"solution": {
"url": "https://www.google.com/",
"status": 200,
"headers": {},
"response": "<!DOCTYPE html>...",
"cookies": [ { "name": "cf_clearance", "value": "...", "domain": ".google.com", "path": "/" } ],
"userAgent": "Mozilla/5.0 ...",
"turnstile_token": null
},
"startTimestamp": 1594872947467,
"endTimestamp": 1594872949617,
"version": "1.6.0"
}solution.headers is empty unless RESPONSE_HEADERS=true, which fills it on both engines or neither. FlareSolverr has never populated it, so the default keeps the payload identical to its.
Like request.get, plus postData.
| Parameter | Notes |
|---|---|
| postData | A string in application/x-www-form-urlencoded form. Eg a=b&c=d. |
Some clients don't consume the solved HTML that /v1 returns. Instead they take the cf_clearance cookie and re-fetch the URL themselves with their own HTTP client. Cloudflare fingerprints that second request (different TLS/JA4, HTTP/2 settings, headers) than the browser that solved the challenge, decides it doesn't match, and re-challenges — so the client fails even though the solve worked. Indexer managers that drive Cloudflare-protected sites are the common case.
The passthrough removes the replay step. Point the client at Solverr's passthrough port instead of the site; Solverr solves in-process (reusing engine fallback, sessions, and per-host memory) and returns the solved body as a clean 200. The client never sees a challenge, so it never re-fetches.
A passthrough request can't pin an engine, so it uses DEFAULT_ENGINE like any other request. That matters for PDFs: they come back as the real file only when the stealth engine solved them (see the PDF note under request.get), so set DEFAULT_ENGINE=stealth if the site serves them.
The target site is the first path segment, and it must be listed in PASSTHROUGH_ALLOWED_HOSTS (anything else gets a 403, so it is never a blind open proxy). A request to:
http://<solverr-host>:8888/example-site.tld/some/path/1/
is solved as https://example-site.tld/some/path/1/. Replace <solverr-host> with wherever Solverr actually runs: its Docker service/container name (e.g. solverr) if the client is on the same Docker network, otherwise Solverr's host IP or hostname. 8888 is PASSTHROUGH_PORT.
A path whose first segment isn't an allow-listed host (the site's own root-relative links, like /details/…, that a client follows for a details or next page) is routed to the default mirror, the first entry in PASSTHROUGH_ALLOWED_HOSTS. That's why downloads and pagination work; it also means the allow-list should be mirrors of one site, not unrelated sites.
Searches can run against any mirror the client picks, but those root-relative follow-ups always land on the default one, so list the mirror you want them served by first. If that mirror goes down, move a healthy one to the front: a client pointed at a working mirror would otherwise still search fine and fail every download.
Enable it with:
environment:
- PASSTHROUGH_ENABLED=true
- PASSTHROUGH_ALLOWED_HOSTS=example-site.tld,mirror.example.tldOn the same Docker network the client reaches it by service name with no published port; from another host, publish PASSTHROUGH_PORT and use Solverr's IP. To make a client's Base URL offer several mirror domains (like a stock indexer definition does), give it one entry per mirror, each prefixed with the passthrough:
http://<solverr-host>:8888/example-site.tld/
http://<solverr-host>:8888/mirror.example.tld/
You don't need a bundled indexer file. Take the site's existing definition from the Prowlarr Indexers repo (or Jackett's), make three changes, and drop it in your manager's custom-definitions folder:
- Change
id:andname:to something unique (e.g. append-passthrough), so it sits alongside the stock one. - Replace the
links:block with one entry per mirror, each prefixed with the passthrough:- http://<solverr-host>:8888/<that-mirror-host>/. - Add every one of those mirror hosts to
PASSTHROUGH_ALLOWED_HOSTS. - Save the file into the manager's custom-definitions folder (create it if it isn't there) and restart the manager:
- Prowlarr:
/config/Definitions/Custom/. TheCustomsubfolder often doesn't exist yet, and Prowlarr ignores YAMLs placed directly inDefinitions/, so createCustom/and put the file there. - Jackett: its custom-definitions folder, which Jackett prints in its startup log (commonly
/config/Jackett/Indexers/custom/on the linuxserver image); create it if missing.
- Prowlarr:
Then add the indexer in the manager, pick a mirror as the Base URL, and do not attach a FlareSolverr/proxy tag — the passthrough already does the solving, and a proxy tag would route around it. Everything else in the definition (search paths, selectors, categories) stays untouched.
Grab the definition as a file, not via copy-paste. A few definitions contain non-printable characters in their filters (a rare title-cleanup step); pasting through a chat or some editors silently strips them and breaks parsing ("No title provided" on every result). Download the raw file so the bytes stay intact.
Notes and limits:
GET/HEADonly; request bodies aren't forwarded. Most indexer definitions areGET.- Encode the mirror as a bare host (
example-site.tld), nothttps://…— clients that normalise//in a path would otherwise corrupt an embedded scheme. - Successful bodies are cached for
PASSTHROUGH_CACHE_TTL; challenge pages and non-2xx responses are not, so a transient block retries rather than sticking. - The cache holds at most
PASSTHROUGH_CACHE_MAX_BYTESin total. The TTL alone bounded how long a body was kept but not how much was kept, so a client walking many pages inside one TTL window could hold all of them at once. - It's still bound by IP reputation like any solve (see Proxy & reliability). If a site blocks your IP, a residential
PROXY_URLapplies to passthrough solves too.
All settings are environment variables and all are optional.
| Variable | Default | Description |
|---|---|---|
DEFAULT_ENGINE |
chrome |
Which engine is tried first for requests that don't set engine (chrome | stealth | auto). Does not disable fallback (that's ENGINE_FALLBACK). |
STEALTH_ENGINE |
true |
Load the Camoufox engine. Set false for a lighter, Chrome-only runtime. |
ENGINE_FALLBACK |
true |
Retry the other engine when the first fails or returns an unsolved challenge. |
STEALTH_HEADLESS |
true |
Run Camoufox headless. |
STEALTH_MAX_ATTEMPTS |
1 |
Click attempts per solver nudge; the engine runs its own wait loop bounded by maxTimeout. |
STEALTH_START_TIMEOUT |
120 |
Seconds allowed to launch a Camoufox browser. |
| Variable | Default | Description |
|---|---|---|
SESSION_TTL_MINUTES |
30 |
Idle minutes before the reaper closes a session's browser (0 disables). |
SESSION_MAX |
20 |
Max concurrent sessions per engine before oldest-idle eviction. |
REAPER_INTERVAL_SECONDS |
60 |
How often the reaper scans. |
MAX_TIMEOUT_MS |
180000 |
Ceiling on a request's maxTimeout (0 lifts it). A larger request is clamped to this with a warning rather than refused, so existing callers keep working. |
| Variable | Default | Description |
|---|---|---|
PROXY_URL |
none | Upstream proxy for both engines. Eg http://127.0.0.1:8080. Overridden by a per-request/session proxy. |
PROXY_USERNAME |
none | Proxy username. |
PROXY_PASSWORD |
none | Proxy password. |
Free click-solving clears the vast majority of challenges, including Turnstile/Managed. This is insurance for the rare site that escalates further: it sends that challenge to a paid solving service (2captcha / CapSolver, ~$3 per 1000 solves) only after free solving has failed, and does nothing until you configure it.
| Variable | Default | Description |
|---|---|---|
CAPTCHA_SOLVER |
none |
Provider: none, 2captcha, capsolver, or another 2captcha-compatible service. |
CAPTCHA_API_KEY |
none | API key. The solver stays dormant unless this and a provider are set. |
CAPTCHA_API_URL |
provider default | Override the 2captcha-compatible host. |
CAPTCHA_API_MAX_ATTEMPTS |
3 |
Polling attempts against the service. |
A second HTTP port that returns solved page bodies directly, for clients that would otherwise re-fetch the URL themselves (see Passthrough proxy). Off by default.
| Variable | Default | Description |
|---|---|---|
PASSTHROUGH_ENABLED |
false |
Turn the passthrough listener on. |
PASSTHROUGH_ALLOWED_HOSTS |
none | Comma-separated hosts it may fetch (the upstream is the first path segment). Empty = refuse every request, so it's never a blind open proxy. |
PASSTHROUGH_PORT |
8888 |
Listening port. |
PASSTHROUGH_CACHE_TTL |
3600 |
Seconds to cache a solved 2xx body (0 disables). Challenge pages are never cached. |
PASSTHROUGH_CACHE_MAX_BYTES |
268435456 |
Ceiling on the total bytes the cache holds (0 lifts it). Past the ceiling the soonest-to-expire entries are evicted first. A single body over a quarter of the ceiling is served but not cached. |
PASSTHROUGH_TIMEOUT_MS |
90000 |
maxTimeout handed to the solver per request. Kept under the ~100s an indexer app waits before recording a failure and backing the indexer off. |
| Variable | Default | Description |
|---|---|---|
HEADLESS |
true |
Run the Chrome engine headless (visible only for debugging). |
DISABLE_MEDIA |
false |
Block images/CSS/fonts by default to save bandwidth (both engines). |
RESPONSE_HEADERS |
false |
Return the page's real response headers in solution.headers instead of an empty map. Both engines, or neither. Off by default: the Chrome engine gets them by having the browser log network events, which has not been measured against a fingerprinting check, and populating the field unasked would change every response. |
BROWSER_WAIT_TIMEOUT |
1 |
Seconds the Chrome engine waits for an expected page state on each attempt. Raise it on a slow host or a slow site. Chrome only: the stealth engine polls until the request's own deadline instead. It never extends maxTimeout. |
BROWSER_GEO |
none | One tag setting the browser's language and timezone. Eg de-DE. See below. |
LANG |
none | Browser language for both engines. Accepts en_US.UTF-8 or en-US. See below. |
BROWSER_TIMEZONE |
auto |
Browser timezone for both engines: an IANA zone, or auto to follow the exit IP. See below. |
LOG_LEVEL |
info |
info or debug. |
LOG_FILE |
none | Also write logs to this file. Eg /config/solverr.log. |
LOG_HTML |
false |
Debug only: log all page HTML at debug level. |
HOST / PORT |
0.0.0.0 / 8191 |
Listening interface and port. Rarely changed under Docker. |
TZ |
UTC |
Container timezone: log timestamps, and the browser's timezone when nothing resolves one. Eg TZ=Europe/London. |
PROMETHEUS_ENABLED |
false |
Enable the Prometheus exporter (see below). |
PROMETHEUS_PORT |
8192 |
Exporter port (expose it if enabled). |
A site can compare the language and clock a browser reports against the country its IP is in, so both engines are always given the same answer for both, from the same lookup. Out of the box you need to set nothing: the timezone and the language are worked out together from the exit IP once and reused, so they always name the same country and a request answered by either engine looks the same.
Set something only when you need a specific result. There are three knobs and they layer:
| Set this | Effect | Costs a lookup? |
|---|---|---|
| nothing | Timezone and language both from the exit IP | once per proxy |
BROWSER_GEO=de-DE |
German, Europe/Berlin |
no |
LANG=de-DE |
German, timezone still from the exit IP | once per proxy |
BROWSER_TIMEZONE=Europe/Berlin |
Europe/Berlin, language unchanged |
no |
BROWSER_TIMEZONE=auto |
Exit IP, ignoring any BROWSER_GEO |
once per proxy |
LANG and BROWSER_TIMEZONE each override BROWSER_GEO for their own half, so BROWSER_GEO=en-US with BROWSER_TIMEZONE=America/Chicago gives American English on Chicago time.
BROWSER_GEO is the short way to match a proxy that always leaves from the same country. It costs no lookup at all, which also makes it the right choice for a deployment with no outbound access beyond its proxy. The timezone it picks is written to the log at startup, because a country with several zones gets its most populous one rather than a fact: BROWSER_GEO=en-US gives America/New_York. Set BROWSER_TIMEZONE if that isn't the one you want. A tag with no country in it, such as fr, sets the language only.
LANG accepts both POSIX and tag forms, and the value is normalized before it reaches a browser:
| You set | Both engines use |
|---|---|
en_US.UTF-8 |
en-US |
de_DE@euro |
de-DE |
pt-BR |
pt-BR |
zh_Hans_CN |
zh-Hans-CN |
fr |
fr |
C, POSIX |
ignored, falls through to BROWSER_GEO then the exit IP |
Anything that isn't a language tag is ignored with a warning in the log rather than passed on. That is deliberate: a malformed value would reach navigator.languages and the Accept-Language header verbatim, which is a more distinctive fingerprint than setting nothing at all. C.UTF-8 is ignored for the same reason, and because it is a container default nobody chose.
Whatever the language ends up being, both engines report it as the two-entry navigator.languages a desktop browser sends: de-DE becomes ["de-DE", "de"].
BROWSER_TIMEZONE takes any IANA zone. Pinning it costs no lookup, so it is also how an air-gapped deployment skips the exit-IP check entirely.
Two things worth knowing. Forcing a language a country doesn't speak, or a timezone it isn't in, is a mismatch a site can see, so change one only if you know why. And some countries share a timezone definition with a neighbour: Norway reports Europe/Berlin and the Netherlands Europe/Brussels, which is correct rather than a bug, since those are the same zone with the same offset and the same daylight-saving rules.
If the exit IP can't be reached, Solverr falls back to the container's TZ for the timezone and en-US for the language, logs a warning, and carries on; it does not fail the request. A SOCKS proxy needs PySocks installed for that lookup to work, and without it you get the same fallback, so pin BROWSER_TIMEZONE or set BROWSER_GEO when using one.
No solver beats Cloudflare by fingerprint alone — IP reputation dominates. A datacenter/VPS IP fails far more challenges than a residential one. If a site keeps failing on both engines, the single most effective fix is a residential proxy: set PROXY_URL (and credentials), or pass proxy per request/session.
Rough guide to expected latency: Chrome solves take a few seconds; Camoufox solves take ~10–20 s (the price of clearing challenges Chromium can't). Session reuse brings follow-ups on the same host down to ~1–3 s.
Disabled by default. Enable with PROMETHEUS_ENABLED=true and expose PROMETHEUS_PORT (default 8192). Metrics include per-domain request counts, results, and duration histograms.
The domain label is capped at 100 distinct hosts; every host after that is reported as other. Prometheus keeps a time series per label value for the life of the process, so an uncapped label would grow the registry with the number of hosts requested. A deployer pointing Solverr at a handful of sites never reaches the cap.
A source shows no results but the log says Challenge not detected! with a 200. An engine loaded the page but couldn't recognise a newer managed/Turnstile challenge and returned it as if solved. Solverr's auto-fallback is designed to catch this and retry on the other engine; make sure ENGINE_FALLBACK is on and the stealth engine is enabled. If it still fails, the site is likely gating on your IP — add a residential proxy.
Out-of-memory / browser launch errors (Proxmox LXC, low-RAM hosts). Give the container more shared memory: shm_size: 512mb in docker-compose.yml (or --shm-size=512m). Reduce SESSION_MAX and keep SESSION_TTL_MINUTES modest so idle browsers are freed sooner.
Camoufox / Firefox errors on ARM or NAS devices. Stealth-engine support on ARM/NAS is best-effort. If it won't launch, set STEALTH_ENGINE=false to run Chrome-only.
Cloudflare has blocked this request / IP banned. Your IP is flagged for that site. Try a (residential) proxy, or open the site in a normal browser from the same network to confirm.
Solverr is licensed under the GNU General Public License v3.0 (see LICENSE). It began as a fork of FlareSolverr (MIT) and its stealth engine derives from Byparr (GPL-3.0); because Byparr is copyleft, the combined work is GPL-3.0. Upstream copyright notices are preserved in NOTICE.