Using SCRUBR as an HTTP(S) proxy
SCRUBR can run as your OS/app HTTP proxy and transparently mask secrets/PII in requests to LLM providers — without changing your app's base URL or API client.
This uses CONNECT-proxy TLS interception (intercept.connect: true): your app
sends SCRUBR CONNECT host:443, SCRUBR terminates TLS for the hosts you've
configured (presenting a certificate minted from your CA), masks the request,
forwards it to the real provider, and rehydrates the response. Every other host
is blind-tunnelled byte-for-byte, so the rest of your traffic is untouched.
flowchart TD
A([App]) -->|"CONNECT host:443 · HTTPS_PROXY"| S{"SCRUBR:<br/>configured host?"}
S -->|yes| M["MITM with a minted cert<br/>mask → forward → rehydrate"] --> P[["LLM provider"]]
S -->|no| T["Blind tunnel · untouched"] --> H[["Origin host"]]
1. Create and trust a CA
./scripts/setup-ca.sh ca
This generates ca/ca.pem + ca/ca.key and installs the cert into your OS trust
store. To do it manually:
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \
-keyout ca/ca.key -out ca/ca.pem -days 3650 -nodes -subj "/CN=SCRUBR CA"
Trust the cert:
| OS | Command |
|---|---|
| macOS | sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ca/ca.pem |
| Linux (Debian/Ubuntu) | sudo cp ca/ca.pem /usr/local/share/ca-certificates/scrubr-ca.crt && sudo update-ca-certificates |
| Linux (RHEL/Fedora) | sudo cp ca/ca.pem /etc/pki/ca-trust/source/anchors/scrubr-ca.pem && sudo update-ca-trust |
| Windows (admin) | certutil -addstore -f ROOT ca\ca.pem |
Firefox, Java, and some apps keep their own trust store — import
ca/ca.pemthere too (Firefox: Settings → Privacy & Security → Certificates → Import).Guard
ca/ca.key— it can mint a trusted certificate for any host. Restrict its permissions and never commit/share it.
2. Run SCRUBR
A ready-to-run config (intercepts OpenAI/Anthropic/Gemini/Mistral, with the curated secret ruleset) is provided:
scrubr --config examples/proxy.yaml
# starts on 127.0.0.1:8443
Add hosts you want masked under routes (matched by host), and content paths
per provider under profiles. Everything else is tunneled untouched.
3. Point your OS/app at the proxy
Per-shell / CLI tools (curl, Python requests, Node, the OpenAI SDKs, …):
export HTTPS_PROXY=http://127.0.0.1:8443
export HTTP_PROXY=http://127.0.0.1:8443
export NO_PROXY=localhost,127.0.0.1 # don't proxy local traffic
System-wide / browsers:
- macOS: System Settings → Network → (your interface) → Details → Proxies →
enable Secure Web Proxy (HTTPS) and Web Proxy (HTTP) →
127.0.0.1 : 8443. - Windows: Settings → Network & Internet → Proxy → Manual proxy setup →
127.0.0.1 : 8443. - GNOME: Settings → Network → Network Proxy → Manual → HTTP/HTTPS
127.0.0.1 : 8443.
4. Verify
HTTPS_PROXY=http://127.0.0.1:8443 \
curl -sS https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" -H "content-type: application/json" \
-d '{"model":"gpt-4o","messages":[{"role":"user","content":"email me at a@b.com"}]}' \
-D - | grep -i x-scrubr
You should see x-scrubr-mode: enforce and x-scrubr-request-id: … response
headers, the provider will have received the email masked as ⟦S:EMAIL·…⟧, and the
reply comes back rehydrated. Enable the transaction log
to inspect exactly what was sent/received (masked).
Caveats
- Only configured
host:routes are masked; all other HTTPS is tunneled and never inspected — so this won't break your general browsing. - Trust is per-process. Env-var proxies cover
curl/Python/Node; OS settings cover browsers and system apps. Whatever makes the request must trust the CA. - HTTPS only in this mode (via
CONNECT); plain-HTTP proxying returns405. LLM APIs are HTTPS, so this is fine. - Certificate-pinned apps (some mobile/desktop clients) reject any minted cert and cannot be intercepted — that's pinning working as intended.
- Start with
masking.mode: dry-runto validate detection before enforcing.
Alternative: SNI-transparent (no proxy setting)
If you can't set a proxy but can control DNS, use intercept.connect: false and
point the target hostnames at SCRUBR (e.g. /etc/hosts); SCRUBR terminates TLS using
the SNI. See DEPLOYMENT.md → TLS interception.