Posted on September 12, 2025
Fine-grained HTTP filtering for Claude Code
Default‑deny HTTP(S) for dev tools and AI agents. Script rules in JS or shell, log every request, and keep egress within your policy.
Coding agents are becoming more powerful every day without commensurate security and governance tooling. The result is a world where solo developers happily run claude --dangerously-skip-permissions for hours unmoderated while many of the world's most important organizations have barely tried agentic developmentLearned from our experience at Coder . I've been working on a tool called httpjail in an effort to make agents available everywhere.
The tool is focused on mitigating these classes of risks:
Risk Example Agents performing destructive actions Deleting your database Agents leaking sensitive information Exposing API keys or credentials Agents operating with more authority than desired Pushing straight to main instead of opening a PR
Agents may transgress accidentally (user misinterpretation) or intentionally (prompt injection).
There is a class of risks at the file-system interface too, but, I believe existing tooling (containers) is sufficient here. Existing network isolation tools rely on IP-based rules. In our case, they're imprecisecentralized, anycast load balancers power much of the internet and require constant maintenanceIPs change randomly and they're not a part of a service's implicit "contract".
httpjail
httpjail implements an HTTP(S) interceptor alongside process-level network isolation. Under default configuration, all DNS (udp:53) is permitted and all other non-HTTP(S) traffic is blocked.
httpjail rules are either JavaScript expressions or custom programs. This approach makes them far more flexible than traditional rule-oriented firewalls and avoids the learning curve of a DSL.
Block all HTTP requests other than the LLM API traffic itself:
$ httpjail --js "r.host === 'api.anthropic.com'" -- claude "build something great"
Privacy minded folks may appreciate this blocks telemetry export to statsig.anthropic.com, which claude pings by default.
Allow only GET requests i.e. make the internet read-only:
$ httpjail --js "r.method === 'GET'" -- claude "build something great"
Only allow hosts in a whitelist.txt file:
$ httpjail --sh "grep -qx \"$HTTPJAIL_HOST\" whitelist.txt" -- claude "research these APIs"
How it works
In a nutshell:
%%{init: {'theme':'dark', 'themeVariables': {'fontSize': '24px'}}}%% graph LR subgraph "1. Setup" direction TB Start{Mode?} Start -->|Linux
Strong| NS[Create namespace
+ nftables redirect
+ setuid $SUDO_USER] Start -->|macOS/--weak
Weak| Env[Set $HTTP_PROXY
env vars] end subgraph "2. Target Process" direction TB Target[Target Process
e.g., claude] Target --> Request[HTTP/HTTPS
Request] Request --> Route{Route
Request} end subgraph "3. Interception" direction TB Proxy[Proxy :8080/:8443] Proxy --> Rules{Evaluate
JS/Script Rules} end subgraph "4. Result" direction TB Internet[✓ Internet Access] Blocked[✗ 403 Blocked] Bypass[⚠️ BYPASSED!
weak mode only] end NS --> Target Env --> Target Route -->|Strong: forced
via nftables| Proxy Route -->|Weak: respects
$HTTP_PROXY| Proxy Route -->|Weak: ignores
$HTTP_PROXY| Bypass Rules -->|Allow| Internet Rules -->|Deny| Blocked style NS fill:#00d4ff,color:#0a0a0a style Env fill:#00d4ff,color:#0a0a0a style Proxy fill:#404040 style Blocked fill:#8b2635 style Internet fill:#2a7f62 style Bypass fill:#8b2635
macOS (Weak Mode)
macOS uses --weak/-w mode by default (see #7).
In weak mode, we rely on process cooperation via the standard HTTP_PROXY / HTTPS_PROXY environment variables. This mode is less of a jail and more of a suggestion that the majority of well-meaning applications happen to comply with.
TLS Interception
httpjail implements full TLS interception to inspect and filter HTTPS traffic. Without interception, rules would only have access to the hostname via SNI, and most of the power of this tool would be lost.
How TLS Interception Works
Certificate Authority Generation: On first run, httpjail generates a self-signed Certificate Authority (CA) that's stored in ~/.config/httpjail/ . This CA is used to sign certificates for intercepted connections. Dynamic Certificate Generation: When a client connects, httpjail : Extracts the Server Name Indication (SNI) from the TLS ClientHello
Generates a certificate on-the-fly for that specific hostname Uses a shared ECDSA P-256 key pair for all server certificates for O(1) keygen overhead
Signs it with the CA certificate
Caches certificate in memory for performance Dual Mode Operation: Transparent Proxy Mode : Directly accepts TLS connections by detecting the TLS ClientHello packet (starts with 0x16 )
: Directly accepts TLS connections by detecting the TLS ClientHello packet (starts with ) Explicit Proxy Mode: Handles HTTP CONNECT tunnels, responds with "200 Connection Established", then transitions to Transparent Proxy Mode Trust Injection: The CA certificate must be trusted by client applications. httpjail automatically sets environment variables for common tools: SSL_CERT_FILE / SSL_CERT_DIR for OpenSSL-based tools
/ for OpenSSL-based tools CURL_CA_BUNDLE for curl
for curl NODE_EXTRA_CA_CERTS for Node.js
for Node.js REQUESTS_CA_BUNDLE for Python requests
for Python requests GIT_SSL_CAINFO for Git
Jail Escapes
In the weak jail, it's trivial for the agent to escape the jail by funnelling requests through a shim program that disregards the HTTP_PROXY environment variable.
Even the strong jail is not perfect. There are potential escape hatches in the filesystem. For example, the agent could create a container via a Docker socket which would spawn outside the network namespace.
To combine filesystem and network isolation into one, httpjail has a --docker-run flag that works like this:
httpjail --js "r.host === 'api.github.com'" --docker-run -- \ --rm alpine:latest wget -qO- https://api.github.com
I believe there's still much value in this approach even with imperfect isolation. In my experience, models seldom try to escape restrictions intentionally placed by the user. And, if the jail is rendered ineffective by prompt injection, it wasn't doing its job in the first place.
Server Mode
For the strongest level of isolation, you can:
Run httpjail --server on a standalone server.
on a standalone server. Configure the network firewall to only permit 80/443 traffic to the proxy server. Optionally, you may redirect all traffic to the proxy server, otherwise you will need to take care in ensuring HTTP_PROXY is set in your environments and all of your web-faring applications respect it.
Run processes as usual in the development environment.
%%{init: {'theme':'dark', 'themeVariables': {'fontSize': '24px'}}}%% graph LR subgraph "Dev Environment" Request[HTTP/HTTPS
Request] Check{Respects
$HTTP_PROXY?} end subgraph "Network Layer" FW[Network Firewall] Config{Config
Mode?} FW --> Config Config -->|Redirect All
80/443 Traffic| Force[Forced to Proxy] Config -->|Allow Only
Proxy IP| Check Check -->|Yes| Voluntary[To Proxy] Check -->|No| Drop[✗ Dropped] end subgraph "httpjail --server" Decision{Evaluate
Rules} end subgraph Result direction TB API[✓ Allowed APIs] Blocked[✗ Blocked] end Request --> FW Force --> Decision Voluntary --> Decision Decision -->|Allow| API Decision -->|Deny| Blocked style Request fill:#404040 style Decision fill:#00d4ff,color:#0a0a0a style FW fill:#404040 style Blocked fill:#8b2635 style Drop fill:#8b2635 style API fill:#2a7f62 style Force fill:#2a7f62 style Voluntary fill:#4a7c59
Try it out
cargo install httpjail
And, check out the GitHub repository for more details.