SearXNG: A Metasearch Engine That Won't Log You

Google's results, minus Google watching you get them

Contents

Every search engine sells the same trade: better results in exchange for a permanent, queryable record of everything you have ever wondered about. That record outlives the reason you searched for it. I have typed symptoms into a search box at 2am that I would rather not have attached to my name in a data broker’s file for the next decade, and so would most people, they just don’t think about it until the moment they’re doing it. SearXNG is the tool I run to break that trade without giving up the quality of results that made Google worth using in the first place.

SearXNG is a metasearch engine: it doesn’t crawl or index anything itself. It takes your query, fires it at a configurable list of upstream engines — Google, Bing, DuckDuckGo, Brave, Wikipedia, and dozens more, including specialised ones for code, academic papers, and torrents — scrapes their public result pages, deduplicates and re-ranks the combined results, and hands you a clean page with no tracking cookies, no query logging, and no ads. It’s a hard fork of the original SearX project, actively maintained since 2021 after the original went quiet, and it’s what most self-hosters run today.

Why aggregation beats picking one engine

Advertisement

The obvious alternative is “just use DuckDuckGo” or “just use Brave Search.” Both are reasonable defaults, but they’re each still a single company’s index with a single company’s ranking bias, and you’re trusting their privacy policy rather than removing yourself from the equation. SearXNG’s model is different: it queries several engines per search, so a niche technical query that Bing handles well and a shopping query Google handles well both come back decent, without you needing to remember which engine is good at what. You get the union of several companies’ crawl budgets without any of them getting the query attached to an account, a cookie, or your IP if you put SearXNG behind a VPN or Tor exit as some deployments do.

The privacy mechanism is simple but effective: SearXNG makes the outbound requests to Google, Bing, and the rest from your server, on your browser’s behalf. Those engines see a single server making anonymised, unauthenticated HTTP requests — no cookies, no browser fingerprint, no account. What they get back is generic result HTML, which SearXNG parses (it maintains scrapers per engine, since none of them offer a clean public API for this) and re-serves to you stripped of every tracking parameter it recognises.

Self-hosting versus a public instance

There’s a free middle option worth naming honestly: SearXNG publishes a list of public instances run by volunteers, and you can just use one of those without deploying anything at all. I don’t, and the reason is trust distribution rather than distrust of any specific instance operator. A public instance sees every query from everyone using it, aggregated behind that operator’s own logging policy, which you have to take on faith the same way you’d take Google’s on faith — you’ve just moved the trust boundary to a stranger running a Docker container instead of a trillion-dollar company. Self-hosting collapses that boundary to zero: the only party that ever sees your query in the clear is your own server, which is the one machine in this entire chain you already had to trust for everything else you run on it. For a single household, the ten minutes of Docker setup is a better trade than distributing that trust to someone else’s instance, however well-intentioned. It also sidesteps a second problem public instances have: they’re the ones absorbing the rate-limit fight described below at a scale that gets them blocked far more aggressively than a low-traffic single-user deployment ever will.

Deploying it

Advertisement

The official image bundles a Python/Flask backend behind uWSGI, and a Redis instance for rate-limiting and caching, so the minimum useful stack is two containers:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# docker-compose.yml
services:
  searxng:
    image: searxng/searxng:latest
    container_name: searxng
    restart: unless-stopped
    ports:
      - "8080:8080"
    volumes:
      - ./searxng:/etc/searxng:rw
    environment:
      - SEARXNG_BASE_URL=https://search.example.com/
      - UWSGI_WORKERS=4
      - UWSGI_THREADS=4
    depends_on:
      - redis
    cap_drop:
      - ALL
    cap_add:
      - CHOWN
      - SETGID
      - SETUID
      - DAC_OVERRIDE

  redis:
    image: redis:7-alpine
    container_name: searxng-redis
    restart: unless-stopped
    command: redis-server --save "" --appendonly no
    volumes:
      - searxng-redis-data:/data

volumes:
  searxng-redis-data:

First run, mount an empty ./searxng directory and the container will populate it with a default settings.yml and uwsgi.ini. Stop the container, edit settings.yml, restart — SearXNG doesn’t hot-reload config changes.

Two settings matter immediately. server.secret_key must be changed from the placeholder to a random value (openssl rand -hex 32 works) or SearXNG will refuse to start in some versions and will log a warning in all of them — it’s used to sign session cookies for things like saved preferences. And server.limiter: true turns on the built-in bot-detection layer, which you almost certainly want enabled the moment this is reachable from the internet, for reasons covered below.

Put it behind a reverse proxy for TLS, same as anything else — see Reverse Proxy Done Right if you haven’t set that up yet — and if you only want it reachable from your own devices, put it on your Tailscale tailnet instead of exposing it publicly at all. I run mine tailnet-only; there’s no reason a search proxy needs a public DNS record.

Tuning engines

settings.yml has an engines: list where every upstream source can be individually enabled, disabled, weighted, or given a category (general, images, videos, news, science, files, IT). I disable everything I don’t use — Yahoo, Startpage’s engine, most of the shopping-specific ones — because every enabled engine is one more outbound request per search and one more thing that can rate-limit you:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
engines:
  - name: google
    engine: google
    shortcut: g
    weight: 2

  - name: bing
    engine: bing
    shortcut: b
    weight: 1

  - name: duckduckgo
    engine: duckduckgo
    shortcut: ddg
    disabled: false

  - name: wikipedia
    engine: wikipedia
    shortcut: wp

  - name: github code
    engine: github
    shortcut: gh

shortcut gives you bang-style queries — !gh nftables docker searches GitHub directly through the same interface, the same trick DuckDuckGo popularised with its !bangs, except here you control the full list and it’s not phoning home to anyone when you use it.

The rate-limit and bot-detection fight

This is the part nobody warns you about before they self-host a metasearch engine, and it’s the reason SearXNG has an entire built-in “limiter” subsystem: Google, Bing, and most large engines actively detect and block scraping, and a self-hosted SearXNG instance making repeated automated-looking requests from a single residential or VPS IP looks exactly like scraping, because structurally it is. Expect Google’s engine to intermittently return CAPTCHA pages instead of results, especially from a datacentre IP with a bit of search volume. There is no clean permanent fix, only mitigations:

  • Enable the limiter (server.limiter: true) so SearXNG rate-limits your own users before they trigger upstream blocks — this protects your one IP’s reputation with Google more than it protects you from anything.
  • Reduce weight on Google specifically and lean more on engines that tolerate automated queries better — Bing and Brave Search have historically been more forgiving; DuckDuckGo’s HTML endpoint is generally reliable since it’s designed for exactly this kind of lightweight scraping.
  • Don’t run it wide open on the public internet. A public instance that anyone can hammer will exhaust your IP’s goodwill with upstream engines within days. Tailnet-only or behind auth is the sane default for a single-user deployment.
  • Set a real User-Agent. SearXNG spoofs a browser UA by default, but if you’re getting blocked disproportionately on one engine, check the per-engine settings.yml overrides — some accept an engine-specific UA string.

Result quality and the categories tab

SearXNG splits results into categories along the top of the page — General, Images, Videos, News, Map, Music, IT, Science, Files — and each category queries a different subset of the engine list. This is more useful than it sounds: the “IT” category weights GitHub, Stack Overflow, and technical documentation sources over the general web engines, which produces noticeably better results for a syntax question than dumping the same query into general search and hoping the right forum post ranks highly. I’ve largely stopped using a separate site-specific search for code questions because !gh or the IT category gets there faster.

The “Map” category deserves a specific mention because it queries OpenStreetMap’s Nominatim by default rather than any Google or Bing mapping product, which means place searches also avoid the location-history logging that comes bundled with commercial map search. For “what’s the postcode of this address” or “is there a hardware shop near here” queries it’s a genuine substitute that never touches a Google account, though it stops well short of a full mapping application.

You can also set per-category default engines and reorder result weighting in settings.yml, so if you find one engine’s technical results consistently better than another’s for your use case, nudge its weight up rather than living with the default balance.

Preferences themselves are worth a mention because they highlight the same privacy pattern running one level down. SearXNG lets you set default categories, preferred languages, safe-search level, and result count through a preferences page, and rather than storing that against an account, it encodes the whole preference set into a single opaque cookie or, if you’d rather not keep even a cookie, a URL parameter you can bookmark. There’s no server-side profile being built up per visitor the way a commercial search engine builds one to justify its ad targeting — the preferences live entirely on your end, in a cookie you control and can clear at will, which is a small detail but a telling one about how differently this tool is designed compared to the sites it replaces.

SearXNG also ships “search syntax” borrowed from the engines it aggregates — quoting a phrase, site: restricting to a domain, - excluding a term — and passes those operators through to whichever upstream engines support them, so query habits built up over years of using Google directly mostly still work unchanged.

Troubleshooting

All results come back empty, no error shown. Check the container logs (docker logs searxng) for engine-specific errors first — this is almost always one or more upstream engines returning a CAPTCHA or a changed HTML structure that broke SearXNG’s scraper for that engine. SearXNG degrades gracefully; if Google is blocking you, you should still get Bing and DuckDuckGo results, so total silence usually means the Redis connection is broken (check depends_on and that the Redis container is actually healthy) rather than every engine failing simultaneously.

“Too many requests” shown to legitimate single-user traffic. The limiter is working, just too aggressively for your usage pattern. limiter.yml (referenced from settings.yml) controls the actual thresholds; for a single-user or household instance, loosen botdetection.ip_limit rather than disabling the limiter outright, which reopens you to the upstream-blocking problem above.

Image search returns nothing while general search works fine. Image results depend on a smaller set of engines that support the images category, and those tend to be blocked more aggressively than general web search because image scraping is a bigger commercial concern for the source sites. Check which engines have images in their category list in settings.yml and make sure at least two or three are enabled.

Config changes not taking effect. SearXNG only reads settings.yml at container start. docker compose restart searxng after every edit; there is no watch-and-reload in production mode.

Is it worth it

Running SearXNG buys you result quality close to Google’s, with zero query logging on your end and no tracking cookies reaching your browser, at the cost of an ongoing low-grade skirmish with upstream engines’ bot detection. For a single household’s search traffic, that skirmish is manageable — the limiter and sensible engine weighting keep it mostly invisible day to day. What you’re really trading is convenience for control: no personalised results, no search history synced across devices unless you build that yourself, and the occasional CAPTCHA wall on one engine while the others keep working. If you already run a Pi-hole to stop tracking at the DNS layer, SearXNG is the natural next step — it closes the gap DNS blocking can’t: the search query itself, which is often the single most revealing thing you send anywhere. And if plain result lists aren’t enough and you want an LLM synthesising answers from those same private searches, I covered pairing a local model with a search backend in Self-Hosted AI Search — SearXNG is one of the search backends that setup can run on.

Advertisement
Advertisement
Smarc
Written by Smarc

Founder and editor of vo.rs. A lifelong tinkerer who self-hosts far more than is sensible, hardens Linux boxes for fun, and prods the latest AI tools to see what they can really do. The how-to guides here are the notes Smarc wishes had existed the first time round.