Repository navigation
HTTP API Reference
The server listens on port 8080 by default. The live OpenAPI schema is served at /openapi.json, and interactive docs at /docs.
Generated from the code: don't edit the table, run uv run scripts/gen-wiki.py.
| Method | Path | Summary |
|---|---|---|
GET |
/ca.der |
The root CA certificate (DER), to install on clients |
GET |
/ca.pem |
The root CA certificate (PEM), to install on clients |
GET |
/healthz |
Liveness: the process is up |
GET |
/metrics |
Prometheus metrics |
GET |
/readyz |
Readiness: the proxy listener is up and the CA is loaded |
GET |
/v1/status |
Version, configuration, health and CA expiry |
/healthz (liveness) answers as long as the process runs. /readyz (readiness) answers 503 while the service can't serve, for example while a dependency is down. Orchestrators should restart on the first and route traffic on the second.
/readyz is 503 ({"status": "starting"}, "stopping" or "stopped") until the proxy listener is up and the root CA is loaded, and again during shutdown.
What's running, without secrets or hostnames (the configuration shows as a hash):
{
"version": "0.1.0",
"mode": "standalone",
"state": "ready",
"started_at": "2026-10-28T14:28:33.156412+00:00",
"config": {"hash": "sha256:a7230c…", "status": "APPLIED", "applied_at": "2026-10-28T14:28:33.156407+00:00"},
"middleware": ["block-ads"],
"health": {
"proxy_listener": {"healthy": true, "status": "listening"},
"ca": {"healthy": true, "status": "loaded"}
},
"certificates": {"ca": {"not_after": "2036-10-25T14:28:33+00:00", "fingerprint": "90:6A:8A:…"}}
}The hash changes when any setting changes, except secrets, which count only as set or unset.
Prometheus text format (text/plain; version=0.0.4). No metric is labelled with a hostname: every label takes its values from a set the proxy declares up front (design 0009), the middleware label from the names in middleware.available (and the built-in capture), and the rule label from capture's rule names, declared when a configuration is applied.
| Metric | Type | Labels |
|---|---|---|
mitm_proxy_build_info |
gauge (1) | version |
mitm_proxy_client_connections |
gauge | |
mitm_proxy_flows_total |
counter |
type: http (intercepted HTTPS), plain_http, websocket
|
mitm_proxy_tunnels_total |
counter |
reason: out_of_scope, not_tls, sni_out_of_scope
|
mitm_proxy_middleware_failures_total |
counter |
middleware: a name from middleware.available
|
mitm_proxy_oversize_passthrough_total |
counter |
direction: request, response
|
mitm_proxy_proxy_auth_failures_total |
counter | |
mitm_proxy_upstream_connections |
gauge |
protocol: http/1.1, h2
|
mitm_proxy_config_applies_total |
counter |
result: applied
|
mitm_proxy_certificate_expiry_timestamp_seconds |
gauge |
kind: ca
|
mitm_proxy_flow_duration_seconds |
histogram |
outcome: origin, local, denied, error, aborted
|
mitm_proxy_upstream_connect_seconds |
histogram |
reused: true, false
|
mitm_proxy_origin_ttfb_seconds |
histogram | |
mitm_proxy_middleware_duration_seconds |
histogram |
middleware: a name from middleware.available
|
mitm_proxy_capture_records_total |
counter |
rule: a capture rule's name; result: written, failed
|
mitm_proxy_oversize_passthrough_total's direction is the body that was too big (request or response), not a flow's direction (inbound or egress, which capture records). mitm_proxy_capture_records_total counts each record the capture middleware wrote, or dropped because it couldn't write it (see Troubleshooting); it has a series only once a record has been counted.
The four histograms cover intercepted HTTP flows only (not WebSockets, not relayed traffic), observed when each flow ends, and are always on. They measure the same things as the access log's total, connect, ttfb and mw (see In-Depth Configuration). The middleware histogram is the one place that includes on_done time. Buckets are fixed: 5 ms to 60 s for the first three, 100 µs to 5 s for middleware.
Some queries:
# The origin's p99, over 5 minutes
histogram_quantile(0.99, sum by (le) (rate(mitm_proxy_origin_ttfb_seconds_bucket[5m])))
# p95 for flows answered by the origin (local answers and aborted downloads excluded)
histogram_quantile(0.95, sum by (le) (rate(mitm_proxy_flow_duration_seconds_bucket{outcome="origin"}[5m])))
# New upstream connections per second, and how long they take on average
rate(mitm_proxy_upstream_connect_seconds_count{reused="false"}[5m])
rate(mitm_proxy_upstream_connect_seconds_sum{reused="false"}[5m])
/ rate(mitm_proxy_upstream_connect_seconds_count{reused="false"}[5m])
# The slowest middleware, by p99
histogram_quantile(0.99, sum by (le, middleware) (rate(mitm_proxy_middleware_duration_seconds_bucket[5m])))
# Capture records dropped, by rule
sum by (rule) (rate(mitm_proxy_capture_records_total{result="failed"}[5m]))
The root CA certificate that clients must trust: PEM (application/x-pem-file, as a download), or DER (application/x-x509-ca-cert, which phones offer to install). It's public: only the private key is secret.
None. The API is read-only: nothing on it changes state or reveals a secret, and it binds to 127.0.0.1 by default. The container manifests bind it to all interfaces inside the pod or container; the published port (Compose) and the NetworkPolicy (Kubernetes) decide who reaches it.
This is the admin API only. Proxy credentials (proxy.auth) protect the proxy port, not this one.
Errors are JSON: {"detail": "<message>"}, with a 4xx status for a client mistake and 5xx for a server-side failure.
| Status | When | What to do |
|---|---|---|
| 404 | Unknown path (including the removed /version and /stats: use /v1/status and /metrics) |
Check the path |
| 405 | A method other than GET
|
Use GET
|
503 {"status": "starting"}
|
The proxy isn't serving yet, or is shutting down (/readyz); or the app runs without a proxy (only when rendering the schema) |
Wait and retry; route no traffic |
The responses the proxy itself sends to clients (403, 407, 502, 504) are listed in Troubleshooting.
mitm-proxy | Async Python MITM proxy with a self-signed CA — HTTP/1.1 & HTTP/2, pluggable middleware for inspecting and rewriting traffic in flight.
GitHub • Releases • Issues • Changelog • Contributing
This wiki is generated from docs/wiki/ on every push to main: edits made here are overwritten. Change it with a pull request.
- 🔍 Intercept only the hosts you list; tunnel the rest untouched
- 🔀 HTTP/1.1 and HTTP/2 on both sides, bridged
- 🧩 Python middleware: inspect, rewrite, answer or deny
- 🔐 Its own root CA; origins always verified
- 📈 Prometheus metrics and a read-only status API