Content
# Tool List
## Table of Contents
## SearXNG + Nginx + Basic Auth One-Click Deployment
Use Docker Compose to deploy [SearXNG](https://github.com/searxng/searxng), a meta-search engine, through Nginx reverse proxy + HTTP Basic Auth to achieve access restriction.
Supports both **HTTP** and **HTTPS** deployment modes, with HTTPS mode automatically identifying user-uploaded SSL certificates.
---
## Prerequisites
The host machine only needs to install Docker (including the `docker compose` subcommand, i.e., Docker Compose v2), without installing any system packages such as `htpasswd`, `openssl`, `apache2-utils`, or `httpd-tools`.
The script will complete all auxiliary operations through the following one-time Docker containers (used and then deleted, not participating in long-term running):
| Operation | Container Image | Description |
| --- | --- | --- |
| Generate `.htpasswd` password file | `httpd:alpine` | Apache official image, providing `htpasswd`, using bcrypt encryption |
| Self-signed certificate / certificate verification | `alpine/openssl` | Provide `openssl`, for modulus pairing verification, expiration check, self-signed generation |
Docker installation documentation: https://docs.docker.com/engine/install/
---
## Project Structure
```
searxng/
├── .env # Environment variable configuration (domain/port/username/password)
├── .env.example # Example file for environment variable configuration
├── docker-compose.yml # HTTP mode deployment file
├── docker-compose.https.yml # HTTPS mode deployment file (TLS + HTTP→HTTPS redirection)
├── setup.sh # One-click deployment script
├── .gitignore
├── mcp-server/ # MCP Server (FastMCP, Streamable HTTP)
│ ├── Dockerfile
│ ├── requirements.txt
│ └── server.py
├── nginx/
│ ├── nginx.conf # Nginx main configuration (worker/gzip/rate limiting)
│ ├── conf.d/
│ │ ├── http/
│ │ │ └── searxng-http.conf # HTTP site configuration (including /mcp reverse proxy)
│ │ └── https/
│ │ └── searxng-https.conf # HTTPS site configuration (TLS security header + /mcp reverse proxy)
│ └── .htpasswd # Basic Auth password file (automatically generated by setup.sh)
├── searxng/
│ └── settings.yml # SearXNG application configuration (search engine/UI/plugins)
└── certs/ # SSL certificate directory
├── fullchain.pem # Certificate / certificate chain (can be a soft link)
└── privkey.pem # Private key (can be a soft link)
```
---
## Environment Variable Description (`.env`)
| Variable | Description | Default Value |
| --- | --- | --- |
| `SEARXNG_PORT` | SearXNG container internal port (no need to modify) | `8080` |
| `HTTP_PORT` | Nginx HTTP external port | `9080` |
| `HTTPS_PORT` | Nginx HTTPS external port | `9443` |
| `DOMAIN` | Domain or IP (used to generate base_url and self-signed certificate CN) | `localhost` |
| `SEARXNG_BASE_URL` | **Important**: SearXNG base URL for generating redirect links | `http://localhost:9080` |
| `AUTH_USER` | Basic Auth username | `admin` |
| `AUTH_PASSWORD` | Basic Auth password (plaintext, encrypted by htpasswd in setup.sh) | `admin123` |
| `CERT_PATH` | HTTPS certificate storage path | `./certs` |
| `LETSENCRYPT_EMAIL` | Let's Encrypt notification email | `admin@example.com` |
| `MCP_HOST` | MCP Server listening address (inside the container) | `0.0.0.0` |
| `MCP_PORT` | MCP Server listening port (inside the container, exposed through Nginx /mcp) | `8000` |
| `OUTGOING_PROXY` | SearXNG outgoing proxy for accessing external search engines. Leave blank for direct connection; `http://host:port` / `socks5h://host:port` | (empty) |
> ⚠️ **`SEARXNG_BASE_URL` must be set**: SearXNG uses it to generate all redirect links (e.g., address after saving preferences). If set incorrectly, you will be redirected to unreachable addresses like `localhost`. Should be set to the complete URL that users actually access, e.g., `http://your-domain:9080` or `https://your-domain.com:9443`.
---
## Quick Start
### 1. Edit `.env` Configuration
```bash
cp .env.example .env && vi .env
```
Minimum required modifications:
```ini
DOMAIN=your-domain.com # or server IP
HTTP_PORT=9080 # HTTP external port
SEARXNG_BASE_URL=http://your-domain.com:9080
AUTH_USER=your_username
AUTH_PASSWORD=your_strong_password
```
### 2. Generate SearXNG Key (optional but recommended)
Generate using the `alpine/openssl` container (no need to install openssl on the host machine):
```bash
docker run --rm alpine/openssl rand -hex 32
```
Fill the output into the `server.secret_key` field of `searxng/settings.yml`.
### 3. Deployment
#### HTTP Mode (quick testing / internal network use)
```bash
chmod +x setup.sh
./setup.sh
```
Access: `http://${DOMAIN}:${HTTP_PORT}`
#### HTTPS Mode (recommended for production environment)
```bash
./setup.sh --https
```
Access: `https://${DOMAIN}:${HTTPS_PORT}` (HTTP automatically redirects to HTTPS)
---
## Using Custom SSL Certificate (HTTPS Mode)
### Certificate File Requirements
Place the certificate and private key files in the `certs/` directory:
| File Type | Extension | Description |
| --- | --- | --- |
| Certificate | `.pem` or `.crt` | Single domain certificate or fullchain certificate chain |
| Private Key | `.key` | PEM format private key |
**Supported private key formats**:
- `-----BEGIN PRIVATE KEY-----` (PKCS#8)
- `-----BEGIN RSA PRIVATE KEY-----` (PKCS#1)
- `-----BEGIN EC PRIVATE KEY-----` (elliptic curve)
**Example**:
```
certs/
├── example.com.key ← private key
├── example.com.pem ← certificate (or fullchain)
├── ca-bundle.pem ← chain certificate (if any)
```
### Automatic Detection Process
When running `./setup.sh --https`, the script will:
1. **Automatically scan** `.key`, `.pem`, `.crt` files under `certs/`
2. **Verify format** — check if it's a valid PEM format
3. **Pairing verification** — compare the modulus of private key and certificate
4. **Expiration check** — expired certificates will error; certificates expiring within 30 days will give a warning
5. **Soft link mapping** — automatically create `fullchain.pem` → your certificate, `privkey.pem` → your private key soft links
6. If **no user certificate is detected**, generate a self-signed certificate (`/CN=<DOMAIN>`)
### Manual Preparation of Certificate
If you don't want to use the script's automatic detection, you can manually create soft links:
```bash
cd certs
ln -sf example.com.key privkey.pem
ln -sf example.com.pem fullchain.pem
```
Nginx will always read `fullchain.pem` and `privkey.pem`.
---
## Manual Deployment (without using setup.sh)
The following commands are executed through Docker containers, and no `htpasswd` or `openssl` needs to be installed on the host machine.
### HTTP Mode
```bash
# 1. Generate password file (bcrypt encryption, through httpd:alpine container)
docker run --rm --user "$(id -u):$(id -g)" \
-v "$(pwd)/nginx:/auth" \
httpd:alpine \
htpasswd -bcB /auth/.htpasswd your_username your_password
# 2. Start service
docker compose up -d
```
### HTTPS Mode (self-signed certificate)
```bash
# 1. Generate password file
docker run --rm --user "$(id -u):$(id -g)" \
-v "$(pwd)/nginx:/auth" \
httpd:alpine \
htpasswd -bcB /auth/.htpasswd your_username your_password
# 2. Generate self-signed certificate (through alpine/openssl container, only for testing)
mkdir -p certs
docker run --rm --user "$(id -u):$(id -g)" \
-v "$(pwd)/certs:/work" -w /work \
alpine/openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout privkey.pem \
-out fullchain.pem \
-subj "/CN=your-domain.com"
# 3. Start
docker compose -f docker-compose.https.yml up -d
```
### HTTPS Mode (using your own certificate)
```bash
# 1. Generate password file
docker run --rm --user "$(id -u):$(id -g)" \
-v "$(pwd)/nginx:/auth" \
httpd:alpine \
htpasswd -bcB /auth/.htpasswd your_username your_password
# 2. Place certificate in certs/ and create soft links
# certs/your-domain.key → private key
# certs/your-domain.pem → certificate (fullchain)
cd certs
ln -sf your-domain.key privkey.pem
ln -sf your-domain.pem fullchain.pem
cd ..
# 3. Start
docker compose -f docker-compose.https.yml up -d
```
---
## Verification of Deployment
```bash
# Without authentication — should return 401 Unauthorized
curl -I http://localhost:9080
# With authentication — should return 200 OK
curl -I -u your_username:your_password http://localhost:9080
# HTTPS verification (accept self-signed certificate with -k)
curl -Ik -u your_username:your_password https://localhost:9443
```
---
## Outbound Proxy (accessing Google / Bing and other external search engines)
When deployed in an internal network / restricted network environment, the SearXNG container may not be able to directly connect to search engines like Google / Bing / DuckDuckGo. After setting `OUTGOING_PROXY`, `setup.sh` will automatically write the proxy into the `outgoing.proxies` field of `searxng/settings.yml`, and all engine requests will go through the proxy.
### Configuration Method
Edit `.env`:
```ini
# HTTP proxy
OUTGOING_PROXY=http://192.168.137.1:10808
# or SOCKS5 proxy (DNS also goes through proxy, recommended)
OUTGOING_PROXY=socks5h://192.168.137.1:10808
# Leave blank = direct connection (default)
OUTGOING_PROXY=
```
Then redeploy:
```bash
./setup.sh # HTTP mode
# or
./setup.sh --https # HTTPS mode
```
`setup.sh` will inject/remove the following fragment into `searxng/settings.yml`:
```yaml
outgoing:
request_timeout: 5.0
useragent_suffix: ""
# Automatically written by setup.sh from OUTGOING_PROXY
proxies:
all://:
- http://192.168.137.1:10808
```
### How to Fill in Proxy Address
| Proxy Location | Fill-in Method | Description |
| --- | --- | --- |
| Proxy on host machine (Clash/V2ray etc.) | `http://host.docker.internal:7890` | compose has configured `extra_hosts: host.docker.internal→host-gateway` for searxng container |
| Another machine on the local network | `http://192.168.137.1:10808` | ensure docker host can ping this IP |
| Proxy container in the same docker network | `http://<service>:port` | e.g., `http://squid:3128` |
> Note: The proxy address must be an address that can be reached from the **SearXNG container perspective**. `localhost` / `127.0.0.1` in the container refers to the container itself and cannot be used.
### Verify Proxy Effectiveness
After deployment, directly call the search interface and check the number of `unresponsive_engines`:
```bash
# Before setting proxy: most engines timeout
curl -s -u admin:admin123 'http://localhost:9080/search?q=test&format=json' \
| python3 -c "import json,sys; d=json.load(sys.stdin); print('unresponsive:', len(d['unresponsive_engines']), '/ results:', len(d['results']))"
# After ./setup.sh: engines return results normally
```
If using MCP, you can also call `searxng_search` tool (see previous chapter), and return non-empty `results` to represent the proxy is effective.
### Troubleshooting
- **Still timeout after proxy**: in the container, verify `docker exec searxng curl -x $OUTGOING_PROXY -sI https://www.google.com`, see if the proxy itself is reachable
- **Some engines timeout**: some engines (like Wikipedia) may not go through the proxy or the proxy chain is unstable, you can disable them in SearXNG preferences or increase `outgoing.request_timeout`
- **socks5 vs socks5h**: `socks5h://` lets DNS resolution also go through the proxy, suitable for scenarios where DNS is polluted; `socks5://` resolves DNS locally
---
## Accessing Claude as MCP
This project has a built-in MCP (Model Context Protocol) Server, based on [FastMCP](https://github.com/jlowin/fastmcp) implementation of **Streamable HTTP** transmission, exposed through Nginx at the `/mcp` path, sharing the site's Basic Auth + TLS.
After deployment, Claude Code / Claude Desktop can be used as an MCP client to directly call SearXNG's search capability.
### Exposed MCP Tools
| Tool | Description |
| --- | --- |
| `searxng_search` | execute meta-search, return aggregated JSON results (support categories/language/time_range/engines etc. parameters) |
| `searxng_config` | get instance configuration (available engines, categories, plugins) |
| `searxng_autocomplete` | get keyword completion suggestions |
### MCP Endpoint Address
| Deployment Mode | URL |
| --- | --- |
| HTTP | `http://<DOMAIN>:<HTTP_PORT>/mcp` (e.g., `http://localhost:9080/mcp`) |
| HTTPS | `https://<DOMAIN>:<HTTPS_PORT>/mcp` (e.g., `https://localhost:9443/mcp`) |
> Note: MCP Server container directly connects to `searxng:8080` through docker network, without going through Basic Auth; external access is uniformly protected by Nginx with Basic Auth. `/mcp` has configured stream-friendly Nginx parameters (`proxy_buffering off`, 300s read timeout).
### Integrating with Claude Code
Edit `~/.claude.json` (or `.mcp.json` for the project), and add to `mcpServers`:
```json
{
"mcpServers": {
"searxng": {
"type": "http",
"url": "http://localhost:9080/mcp",
"httpAuthenticationHeaderName": "Authorization",
"headers": {
"Authorization": "Basic <BASE64(user:pass)>"
}
}
}
}
```
Generate the Basic credentials header (replace `admin:admin123` with your actual username and password):
```bash
echo -n "admin:admin123" | base64
# Output example: YWRtaW46YWRtaW4xMjM=
```
Fill the value into `headers.Authorization` in the format `Basic <output>`.
### Connecting to MCP with a Self-Signed HTTPS Certificate
The MCP HTTP client in Claude Code / Claude Desktop verifies TLS certificates by default. If SearXNG is deployed in HTTPS mode with a **self-signed certificate** (e.g., generated by `setup.sh --https` with `/CN=<DOMAIN>` or an internal self-signed certificate), the client will fail to connect because the certificate is not in the system's trust chain. Typical symptoms:
- MCP tool is unavailable in Claude Code, with a connection failure status
- Logs/Console show `unable to verify the first certificate` / ` certificate` / `ERR_CERT_AUTHORITY_INVALID`
There is no in Claude Code's MCP to disable TLS verification (no.noVerify` / `rejectUnauthorized` / `sslValidate Optional solutions are listed below, in order of recommendation:
#### — Add to System Trustmost secure, recommended)
self-signed certificate to the directory and update the trust librarybash
# Debian/Ubuntu
sudo cp certs/fullchain.pem /usr/local/share/caarxng.crt
sudo update-ca-certificates
# RHEL/CentOS
certs/fullchain.pem /etc/pki/ca-trust/source/searxng.crt
sudo update-ca-trust
Claude Code still reports a (Node's built-in trust be out of sync with the system), add the following to the top-level `env` in `~aude/settings.json` (**restart Claude Code for it to take effect**):
```json
{
"env": {
"NODE_EXTRA_CA_CERTS": "/usr/local/share/ca-certificates/searxng.crt"
}
}
```
`NODE_EXTRA_CA_CERTS` does not bypass verification but appends the specified certificate to the Node trust chain is safer than disabling#### Solution B — Globally TLS Verification (testing)
Inject into the top-level `env` in `claude/settings.json`:
{
"env "NODE_TLS_REAUTHORIZED": "0 }
}
```
**Risk make all HTTPS outbound connections from the Claude Code main process ( the Anthropic API certificate verification, posing a risk of man-in-the-middle attacks only for **isolated internal testing environments**; be sure to remove it when done.
> After modifying `env` in ``, you must **restart Claude Code** (exit CLI/close window and restart); environment variables are read at startup and do not take effect in the current session.
#### Solution C — Switch to HTTP Mode (fastest, internal network only)
If it's just for internal network testing and no TLS is needed, using the HTTP endpoint is the easiest way to avoid certificate issues:
```bash
./setup.sh # Without --https, use HTTP
```
Then change `searxng.url` in `~/.claude.json` to:
```json
"url": "http://<DOMAIN>:<HTTP_PORT>/mcp"
```
#### Solution D — Use a Legitimate Certificate Signed by a CA
Place your legitimate certificate in `certs/` and create a soft link, then redeploy:
```bash
cd certs
ln -sf your-domain.key privkey.pem
ln -sf your-domain.pem fullchain.pem
cd ..
./setup.sh --https
```
A legitimate certificate issued by a trusted CA by Claude Code without additional configuration Integrating with Claude Desktop
Claude Desktop's `claude_desktop_config.json` alsotype: "http"` configuration, with the same fields as above. Note that Desktop has limited support for self-signed HTTPS certificates; it's recommended to use a legitimate certificate in HTTPS mode or access locally in HTTP mode.
### Verifying the
After deployment, use` to directly verify the Streamable HTTP endpoint (a valid `initialize` JSON-RPC callbash
curl -N -u admin:admin123 http://9080/mcp \
-H "Content-Type: application/json" \
-H: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"manual","version":"0"}}}'
```
A successful response will contain an SSE stream with `"result"`; common failure reasons:
- `401`: Basic Auth username/password incorrect
- `502/504`: MCP container not started; check `docker compose logs searxng-mcp`
- HTML response: incorrect path ( be `/mcp`,`)
### Manual Testing ToolAfter completing the `initializenotifications/initialized` handshake, invoke `tools/call`:
```bash
# Get the Mcp-Session-Id header returned in the previous step, then:
SESSION=<id returned in the previous step>
curl -N -u admin:admin123 http://localhost:9080/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Mcp-Session-Id: $SESSION" \
-d '{"jsonrpc":"2.id":2,"method":"tools/call","params":{"name":"searxng_search","arguments":{"query":"OpenAI GPT","categories":"general","language":"zh-Hans"}}}'
```
A more convenient way: after connecting with Claude Code, simply say "search with sear_search ..." in the conversation.
---
## Rate Limiting
Nginx has configured rate limiting for the search interface:
| Value |
|------|----|
scope | `/search` path |
| Rate | 10 requests per second per IP |
| Burst | 20 requests (exceeding part processed immediately) |
| Configuration location | `nginx/nginx.conf` → `limit_req_zone` directive |
---
## Common Management Commands
```bash
# View all service logs
docker compose logs -f
# View individual service logs
docker compose logs -f searxng
docker compose logs -f nginx
# Restart services
docker compose restart
# Stop and remove containers
docker compose down
# Update images and rebuild
docker compose pull
docker compose up -d
# Regenerate . and restart Nginx only (passwd installation needed on the host)
docker run --rm --id -u):$(id -g)" \
-v "$(pwd)/nginx:/auth" \
httpd:alpine \
htpasswd -bcB /auth/.htpasswd new_username new_password
docker compose restart nginx
# View container status
docker compose ps
```
---
## SearXNG Configuration Reference (`searxng/settings.yml`)
| Configuration | Description values |
|--------|------|--------|
| `server.secret_key` | Session encryption key | generated by `openssl rand -hex 32` |
| `server.bind_address` | Listen address | `0.0.0.0` |
| `server.port` | Internal port | `8080` |
| `server.image_proxy` | Proxy search result images | `true` / `false` |
|.safe_search` | Safe search level | `0`=off, `1`=moderate, `2`=strict |
| `search.autocomplete` | Search suggestion API | `""`=off, `"google"`, `"duckduckgo"` etc. |
| `search.default_lang` | Default search language | `""`=auto, `"zh-Hans"`, `"en"` etc. |
| `search.formats` | Output formats | `html`, `json`, `csv`, `rss` |
| `ui.default_theme` | Default theme | `simple` |
| `ui.default_locale` | Default interface language | `""`=auto, `"zh-Hans"`, `"en"` etc. |
| `ui.static_use_hash` | URL hash versioning for static resources | `true` |
Complete documentation: [https://docs.searxng.org/admin/settings://docs.searxng.org/admin/settings## Security Recommendations
1. **Change the default password** sure to modify `AUTH_USER `AUTH_PASSWORD` in `.env`
2. **Randomize secret_key** — run `docker run --rm alpine/openssl rand -hex 32` to replace the key in `searxng/settings.yml`
3. **Correctly set SEARXNG_BASE_URL** — otherwise, saving preferences will redirect to an incorrect address
4. **Use a CA-signed certificate in production** — do not use self-signed certificates; place `.key` / `.pem` in `certs/`, and the script will automatically recognize them
5. **Enable the firewall** — expose only Nginx ports (`HTTP_PORT` / `HTTPS_PORT`) and do not directly expose the SearXNG 8080 port
6. **Enable HSTS** (HTTPS mode) — uncomment the `add_headerTransport-Security` line in `nginx/conf.d/https/searxng-https.conf`
7. **Regularly update images** — `docker compose pull && docker compose up -d`
---
## Troubleshooting
### Saving Preferences Redirects to localhost
Check if `SEARXNG_BASE_URL` in `. is. It should be set to the complete URL that the user actually accesses:
```ini
# Incorrect: redirects to localhost
SEARXNG_BASE_URL=http://localhost:9080
# Correct: set to your actual domain or IP + port
SEARXNG_BASE_URL=http://your-server.com:18001
```
Restart containers after modification:
```bash
docker compose down && docker compose up -d
```
### Certificate Key Pair Mismatch
reports a `key mismatch`, check if the certificate and private key match (using the `alpine/openssl` container, no need to install openssl on the host):
```bash
# Compare modulus — output of both commands should be identical
docker run --rm -v "$(pwds:/work" -w /work alpine/openssl x509 -noout -modulus -in fullchain.pem | md5sum
docker run --rm -v "$(certs:/work" -w /work alpine/openssl rsa -noout -modulus -in privkey.pem 5sum
```
### Port Occupied
HTTP_PORT` / `HTTPS_PORT` in `.env another port, then restart.
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available