SSD Nodes Learn Hosting plans →
Guides Matt ConnorBy Matt Connor

Fix SearXNG CAPTCHA errors from engines

SearXNG engines answer a VPS with CAPTCHA pages more often than a home connection. Read the real error, then pick a fix that survives a restart.

What a SearXNG CAPTCHA error means

SearXNG CAPTCHA errors come from the engines your instance queries. Your server asked an engine for results, the engine replied with a challenge page instead of results, and SearXNG recorded an error against that engine because there was nothing in the reply to parse. Your instance is healthy. A machine you do not control decided your request did not look like a person.

That one fact decides every fix below. The decision was made on the engine's own hardware, so nothing in your settings.yml can overrule it. What you can change is which address the request leaves from, which engines you query at all, and how your instance behaves once an engine starts refusing.

Two failures that look the same, and how to tell them apart

The first failure is your own instance answering HTTP 429 (too many requests) to your own browser. That is the SearXNG limiter, the bot detection layer that sits in front of the search endpoint. It runs on your box and it is yours to configure. the limiter that returns 429 to your own users is a separate problem with separate settings, and none of the advice below applies to it.

The second failure is upstream. The results page loads normally, and one or more engines are missing from the results or carry an error notice. Nothing on your instance refused anything. An engine refused your server.

  • The page will not load, or the search endpoint answers 429: look at your limiter.
  • The page loads and results are thin, or an engine is flagged with an error: look upstream, and keep reading.

Both can happen on the same instance, and they feed each other, because a limiter that is set too loose lets in traffic that pushes your outgoing query rate higher. Diagnose them one at a time.

Why do SearXNG engines return CAPTCHA errors on a VPS and not on my laptop?

Because of the address the request comes from. Your home connection has an address from a consumer ISP (internet service provider) range, shared over time with many ordinary people. Your VPS has an address from a datacentre range, and those ranges are published: anyone can look up which addresses belong to a hosting provider. An engine that wants to keep scrapers out starts by treating requests from hosting ranges as suspect, because very little in those ranges is a human with a browser.

A few other things stack on top of the address. Your instance sends one request per engine per user search, so even a small number of users produces a rate from a single address that no single person produces. SearXNG keeps no session with the engine and carries no long lived cookie, by design, so every request arrives with no history behind it. And the address may carry history you did not create, because providers recycle addresses and the previous tenant may have been scraping from it for months.

The refusal itself is not always an obvious failure. An engine can answer with 403, with 429, or with HTTP 200 and a challenge page in the body. That last case confuses people, because a status code check says the engine is fine while SearXNG finds zero results in the response. This is why you read your own instance's error report instead of curling the engine and looking at the status line.

Read what your instance reports before you change anything

Every fix below starts with the name of the engine that is failing and the reason your instance recorded for it. SearXNG exposes both. The /stats page lists engines with their error counts and reliability, and /stats/errors returns the error detail as JSON, which is easier to keep and to compare next week. Open them in the browser you normally use for the instance.

The container log carries the same events as they happen. The service name here is the one used in the compose file published with the container documentation, so use yours if it differs.

docker compose logs -f core

Run a search that fails while the log is following. You should see an entry for the failing engine appear as the search runs. Write down the engine name and the exact reason string your instance printed. Do not copy an engine name out of a blog post, including this one. The set of engines that challenge datacentre addresses changes from month to month, and the engine that fails for you may work perfectly for whoever wrote the post you are reading.

If the results page shows no error at all but the results are thin, check display_error_messages for that engine. It defaults to true, and an instance that has turned it off is hiding the one message you need.

How SearXNG retries and suspends a failing engine

SearXNG does not keep hammering an engine that refuses it. A failing engine is suspended, and while it is suspended it is skipped entirely, which is how a broken engine turns into a silently missing engine.

Two layers control this, and both live under search: in settings.yml. Confirm these key names against the settings documentation for the version you actually run before you paste anything, because they have moved between releases. As documented on 2 September 2026, the defaults are:

search:
  ban_time_on_fail: 5
  max_ban_time_on_fail: 120
  suspended_times:
    SearxEngineAccessDenied: 86400
    SearxEngineCaptcha: 86400
    SearxEngineTooManyRequests: 3600
    cf_SearxEngineCaptcha: 1296000
    cf_SearxEngineAccessDenied: 86400
    recaptcha_SearxEngineCaptcha: 604800

The first layer handles ordinary failures such as a timeout. The ban starts at ban_time_on_fail seconds and grows with each consecutive failure, up to max_ban_time_on_fail. Two minutes is the ceiling by default, so a flaky engine recovers on its own within a few minutes of the problem going away.

The second layer handles the failures this guide is about. When SearXNG recognises the reply as a challenge or a refusal rather than a generic error, it applies the matching entry from suspended_times, and those numbers are much larger. 86400 seconds is a full day. 604800 is a week. 1296000 is fifteen days. The keys prefixed cf_ apply when the challenge is recognised as a Cloudflare one, and recaptcha_ when it is recognised as reCAPTCHA.

That explains the symptom which wastes the most time. You find the cause, you fix it, and the engine still returns nothing for hours. It is still suspended. The suspension is held in the running process, so restarting the container clears it and the next search tries the engine again. A plain restart does the job here, and it is worth knowing when a restart is enough and when you need to recreate the container before you start rebuilding images for no reason. If the engine fails again immediately after the restart, your fix did not work.

One per engine setting deserves a warning. retry_on_http_error retries a request when the engine answers with the status codes you list. Against an engine that is blocking you, retries send more traffic to the system that already decided your server is a bot. Leave it alone unless you are working around an engine that is genuinely intermittent.

The SSH tunnel upstream documents, and what it does not fix

Checked on 2 September 2026, the SearXNG admin documentation answers this problem with a manual tunnel. You open a SOCKS proxy through your server, point your desktop browser at it, and answer the challenge by hand while the engine sees the server's address.

ssh -q -N -D 8080 user@example.org

-D 8080 opens a local SOCKS server on port 8080 that forwards through the SSH connection. -N runs no remote command and -q keeps it quiet, so a healthy tunnel prints nothing and does not return. Check it from a second terminal:

curl -x socks://127.0.0.1:8080 http://ipecho.net/plain
curl http://ipecho.net/plain

The first command should print your server's address and the second your desktop address. Two identical answers mean the request is not going through the tunnel. Then set your browser's network settings to a SOCKS5 proxy at 127.0.0.1 port 8080, load the same address checker in the browser to confirm it reports the server, and visit the engine that is challenging you. Answer the challenge there.

Now the honest part. Four things limit this method. The cookie the engine hands out lands in your desktop browser, and SearXNG has no access to your browser's cookies, so the only thing that can help your instance is whatever the engine records against the address itself. That record expires, on a schedule the engine chooses and does not publish. No part of the procedure is automated, so you are back at the keyboard next time. And on an instance other people use, the query rate that triggered the challenge is still running, so the challenge comes back.

Use it to get one instance working this afternoon. Do not build an instance around it.

Fix that lasts: drop or reweight the engines that block you

The cheapest durable answer is to stop querying an engine that will not serve your server. Your settings.yml starts with use_default_settings: true in the container image, which means an entry under engines: with a matching name overrides only the keys you list and leaves the rest of the default definition alone.

use_default_settings: true

engines:
  - name: <engine name from your stats page>
    disabled: true
  - name: <another engine name>
    weight: 0.3

disabled: true turns the engine off by default while leaving it on the preferences page, so a user who wants it can switch it back on for their own searches. inactive: true removes it from user settings entirely, which is what you want for an engine that will never work from your address. weight does a different job: it scales how much that engine's results count when SearXNG merges and ranks them, so a weight below 1 keeps a marginal engine without letting it take over the first page.

Restart the container after editing, run a few searches, then check /stats again. A clean stats page with six working engines is more useful than a page full of errors with twenty.

Fix that lasts: send outgoing requests through a proxy

SearXNG can send its outgoing engine requests through a proxy, which changes the address the engine sees. Set it globally under outgoing:, or per engine when only one engine is the problem.

outgoing:
  request_timeout: 2.0
  extra_proxy_timeout: 10.0
  proxies:
    all://:
      - socks5h://user:password@proxy:1080
engines:
  - name: <engine name>
    proxies:
      http: socks5h://user:password@proxy:1080
      https: socks5h://user:password@proxy:1080

Prefer socks5h:// over socks5:// when you want the proxy to resolve the hostname, because the h means the name is sent to the proxy instead of being looked up on your server. Raise the timeout budget at the same time. request_timeout defaults to 2.0 seconds, a proxy adds a round trip to every request, and engines that used to answer in time start failing as timeouts instead. extra_proxy_timeout exists for exactly this and adds seconds when a proxy is in use.

What a proxy costs you:

  • The proxy operator sees which engines your instance queries and when. TLS (transport layer security) keeps the search terms out of their logs, because the query sits inside the encrypted request, but the shape and timing of your traffic is theirs to read.
  • A shared exit address is shared with whoever else is paying for it. If they scrape, you inherit their reputation, sometimes faster than the block you were escaping.
  • Cheap residential proxy pools are often built from consumer devices whose owners did not knowingly agree to carry traffic. Know what you are buying.
  • using_tor_proxy: true routes through Tor, but exit node addresses are published in full, and an engine that challenges datacentre ranges usually challenges exit nodes at least as hard.
  • Search now depends on a service outside your server, which can fail on its own schedule and take your results with it.

A proxy moves the block rather than removing it, and the privacy story of your instance now includes a third party. If a short privacy story is why you self-host, weigh that against what a self-hosted instance really hides and what it does not before you sign up for anything.

Fix that lasts: run a smaller engine set on purpose

The option most people skip is accepting fewer engines. The value of SearXNG is the merge, and a merge of six engines that answer every time beats twenty where half are suspended for a day at a stretch. Watch /stats for a week and keep the engines with a clean record from your address.

Engines you authenticate to with an API key behave differently, because the engine knows who you are and enforces a quota instead of guessing whether you are a person. The trade is an account, a key sitting in your settings file, and usually a bill. For one or two engines that matter to you, that is often the least painful path.

Decide this with your other tools in mind. A suspended engine is invisible to anything reading results over the API, because the JSON API that Open WebUI and similar tools query simply returns fewer results rather than an error your tool can notice. If something automated depends on your instance, poll /stats/errors on a schedule instead of waiting for someone to complain that the answers got worse.

Is this worth fighting at all?

Answer that by counting users. An instance for one person sends a handful of searches a day from one address, a rate that many engines never challenge. When one does challenge you, the fix is cheap: drop the engine and you will barely notice it is gone. That is the ordinary experience of running SearXNG for yourself on a small VPS, and it needs no tunnel and no proxy.

A public or shared instance is a different machine running the same software. The query rate is the trigger, and it grows with every user you add, so challenges arrive faster than any configuration can absorb. Plan for a smaller engine set from the start, and remember that any proxy you add now carries other people's searches under your account.

Automated clients sit in between and lean toward the harder case. An agent that runs several searches to answer one question produces bursts no human produces, so an instance you point coding agents and research tools at meets challenges sooner than the same instance driven by hand. If that is your use, pick the engine set on reliability rather than breadth, and let the agent work with results it can actually get.

The rule that holds: fight for an engine when it is the reason you self-host, and drop it when it is not.

FAQ

Why does a SearXNG engine still return nothing after I fixed the problem?

Because it is still suspended. When SearXNG recognises a challenge or a refusal from an engine, it stops querying that engine for the period set in search.suspended_times, and those defaults run from one hour to fifteen days depending on the type of refusal. The suspension is held in the running process, so restarting the container clears it and the next search tries the engine again. If the engine fails once more right after the restart, your fix did not work.

Is an engine CAPTCHA error the same as the 429 my instance returns?

They travel in opposite directions. A 429 from your instance to your browser is SearXNG's own limiter deciding that your request looked automated, and it is yours to configure. A CAPTCHA or block error is an upstream engine refusing your server, decided on hardware you do not control. If the results page loads and only some engines are missing, you are looking at the second one.

Will a VPN or a proxy on my server fix engine CAPTCHAs?

Sometimes, and it costs something. Routing outgoing requests through outgoing.proxies changes the address the engine sees, which can clear a block that was tied to your datacentre range. The proxy operator then sees which engines you query and when, a shared exit address arrives with other customers' reputations attached, and the added latency causes timeouts unless you raise request_timeout and extra_proxy_timeout. Tor is available through using_tor_proxy, but exit addresses are published and widely challenged.

Can I make SearXNG solve the CAPTCHA automatically?

There is no setting for it. The method the project documents is manual: an SSH SOCKS tunnel, your own browser, and your own hands on the challenge. Anything you build to answer challenges automatically works against the engine's stated policy and breaks quietly every time the challenge changes, which leaves you maintaining a scraper instead of running a search instance. Removing the engines that block your address is the answer that keeps working.