
Go-based MITM HTTP/HTTPS proxy with HTTP/2 and HTTP/1.1 interception, local CA/per-host cert generation, CONNECT/WebSocket tunneling, disk caching, admin dashboard, traffic capture, block policies, and optional AI-backed threat scanning with redaction, quarantine, and audit logging.
A lightweight, developer-friendly Man‑in‑the‑Middle (MITM) HTTP/HTTPS proxy written in Go. It supports HTTP/1.1 and HTTP/2, CONNECT tunneling, WebSocket tunneling (ws/wss), on‑disk response caching with flexible filters, and live config reloading.

Go MITM Proxy is an intercepting proxy intended for debugging, testing, learning, and controlled interception of HTTP(S) traffic. When MITM is enabled, it dynamically generates per‑host leaf certificates signed by a local CA, allowing the proxy to decrypt and inspect HTTPS traffic. It can also work as a transparent TCP tunnel when MITM is disabled or for excluded domains/ports.
Important: This application performs active Man-in-the-Middle (MITM) interception, including the generation and use of TLS certificates to decrypt HTTPS traffic. Depending on your jurisdiction and network environment, intercepting traffic without clear, prior consent from all affected users may be illegal and can violate privacy, workplace policy, or regulatory requirements.
Before using this software in any environment other than your own local machine:
All users of any network where this proxy may intercept traffic must be clearly informed that HTTP(S) interception and inspection will occur. Consent should be explicit and ideally documented.
Do not run this software on networks you do not own, administer, or have explicit authorization to test or monitor.
Many regions have strict laws governing the interception, logging, and storage of user data (e.g., GDPR, CCPA, wiretap laws). You are responsible for ensuring your use complies with all applicable regulations.
The generated CA private key (usually ca-key.pem) allows the holder to impersonate any domain for users who trust the corresponding certificate.
This proxy is designed for development, debugging, controlled testing, or educational purposes — not for covert monitoring or unauthorized surveillance.
By using this software, you acknowledge and accept full responsibility for ensuring your usage is lawful, ethical, and properly communicated to all affected users.
Prerequisites:
Clone and build:
git clone https://github.com/Welfordian/mitm-proxy.git
cd mitm-proxy
go build ./
This produces a mitm-proxy (or mitm-proxy.exe on Windows) binary in the project root.
./mitm-proxy
On first start, a local CA will be created and saved to ca-cert.pem and ca-key.pem.
Configure your browser or curl to use the proxy at http://localhost:8080.
Trust the generated CA certificate (ca-cert.pem) in your OS/browser to allow HTTPS interception. See Trusting the Local CA.
Visit an HTTPS site through the proxy and observe logs. Use verbose mode for more detail:
./mitm-proxy --verbose
CLI flags override configuration file values where noted.
An example config.json is included in the repo:
{
"listen_addr": ":8080",
"proxy_name": "MITM-Proxy",
"ca_cert_path": null,
"ca_key_path": null,
"ca_cert_output_path": "ca-cert.pem",
"ca_key_output_path": "ca-key.pem",
"enable_mitm": true,
"admin_enabled": true,
"admin_addr": "127.0.0.1:9090",
"admin_token": "",
"admin_read_token": "",
"admin_ui": true,
"admin_store": "dashboard.db",
"excluded_domains": [],
"blocked_ports": [25, 445, 3389],
"blocked_domains": [],
"blocked_ips": [],
"block_action": "deny",
"block_response_status": 403,
"traffic_capture": {
"store_bodies": false,
"max_body_bytes": 32768,
"redact_bodies": true,
"store_headers": true,
"redacted_headers": ["Authorization", "Cookie", "Proxy-Authorization", "Set-Cookie", "X-Api-Key"],
"store_cookies": true,
"redacted_cookies": []
},
"proxy_auth": {
"enabled": false,
"realm": "MITM Proxy",
"require_auth_for_loopback": false,
"default_action": "allow"
},
"verbose_logging": true,
"log_requests": true,
"max_idle_conns": 200,
"idle_conn_timeout_seconds": 90,
"tls_handshake_timeout_seconds": 10,
"min_tls_version": "1.2",
"tls_next_protos": ["h2", "http/1.1"],
"cache": {
"enabled": true,
"directory": "/var/cache/mitm-proxy",
"include_domains": [],
"exclude_domains": [],
"include_extensions": ["jpg", "png", "webp", "css", "js"],
"exclude_extensions": [],
"ttl": 3600
}
}
Notes:
The admin server serves the dashboard at http://127.0.0.1:9090/admin/ by default. API routes require Authorization: Bearer <token>; for local browser use, /admin/?token=<token> stores the token in browser local storage.
Initial dashboard/API coverage includes:
The dashboard includes a first-run responsible-use confirmation. The CA private key is never exposed through the admin API.
Dashboard state is stored in SQLite at dashboard.db by default. Settings changed through the dashboard are applied immediately and written back to the configured JSON file, or to config.json when the proxy was started from defaults.
The admin frontend is a Vite/React app in internal/admin/ui. Its production build is emitted to internal/admin/ui/dist and embedded into the Go binary. To update the dashboard assets:
cd internal/admin/ui
npm install
npm run build
Outbound traffic can be chained through an upstream HTTP or HTTPS proxy, such as Burp, ZAP, or a corporate egress proxy. When enabled, normal HTTP(S) forwarding, CONNECT pass-through tunnels, WebSockets, and Repeater sends use the upstream proxy unless a host matches no_proxy.
{
"upstream_proxy": {
"enabled": true,
"url": "http://127.0.0.1:8080",
"username": "",
"password_env": "UPSTREAM_PROXY_PASSWORD",
"no_proxy": ["localhost", "127.0.0.1", "*.internal"],
"chain_tunnels": true,
"apply_to_repeater": true
}
}
Only http:// and https:// upstream proxy URLs are supported in v1. If Basic auth is needed, set username and provide the password through the named environment variable; credentials embedded in the URL are rejected and are never shown in dashboard settings. If the upstream proxy is enabled but unavailable, affected requests fail visibly instead of silently falling back to direct connections.
The dashboard's Access Control view manages client proxy users and ordered allow/deny ACL rules. Proxy users are stored in SQLite with bcrypt password hashes; plaintext passwords are accepted only when creating or resetting a user and are never returned by the API.
Enable Basic proxy authentication through proxy_auth in config.json or the Settings view. When enabled, clients must send Proxy-Authorization: Basic ... unless loopback clients are exempt. ACL rules are evaluated by priority and can match username, source IP/CIDR, host or wildcard host, port or port range, method, and research scope. Empty matcher lists mean "any".
Proxy-Authorization is stripped before forwarding, upstream chaining, traffic capture, cache lookup, threat scanning, and Repeater cloning. Captured traffic includes proxy_user attribution when available, and the Traffic search box can match proxy usernames.
The dashboard's Repeater view lets security researchers clone captured HTTP traffic into saved editable cases. A case stores the method, URL, headers, body sample, timeout, and optional source traffic flow ID. Each send stores a run with status, duration, response headers, a capped response body sample, and any upstream error.
Captured request bodies are only prefilled when traffic_capture.store_bodies was enabled at capture time. If body redaction was enabled, the repeater receives the redacted sample; uncaptured bodies remain empty and can be edited manually.
The legacy POST /api/traffic/{id}/replay endpoint remains available for one-shot replay, while the repeater is intended for repeatable request mutation and response comparison.
The dashboard's Pentest Toolkit view builds passive target maps from captured traffic. Rebuilding a map analyzes only stored traffic for the selected scope, groups endpoints by normalized path, extracts query/body/cookie/header parameters, records reflected and interesting parameters, and adds passive hints such as missing security headers, cookie attribute gaps, permissive CORS, and verbose errors.
Pentest maps are persisted in SQLite and can be deleted independently. The toolkit never sends requests, crawls, fuzzes, or mutates targets; endpoint evidence can be cloned into Repeater for manual testing.
The dashboard's Scopes view lets researchers define named target boundaries with host, URL substring, and optional method patterns. Enabled scopes are matched automatically when traffic is captured; matching flows, cloned Repeater cases, and threat scanner events receive a single scope_id.
The global scope selector filters Traffic, Repeater, and Threat Scanner views across all traffic, a selected enabled scope, or out-of-scope items. Deleting a scope clears related scope_id values without deleting captured traffic, Repeater cases, runs, or threat data.
Scope filters are available on GET /api/traffic, GET /api/repeater/cases, and GET /api/threats/events with scope_id=<id> or scope_id=__out_of_scope__. Add include_out_of_scope=true to include unscoped rows beside a selected scope.
The dashboard's AI Copilot view stores AI-generated research notes linked to Traffic, Repeater cases, runs, scopes, or threat events. Traffic detail can ask the copilot to explain a request or suggest next manual tests; Repeater can suggest tests for a saved case or compare the latest two runs.
The copilot is advisory only. It never sends traffic, edits Repeater cases, changes scopes, changes settings, or purges data. Out-of-scope traffic can be explained, but active testing suggestions are intentionally withheld.
Enable it through ai_copilot in config.json or the Settings view:
{
"ai_copilot": {
"enabled": true,
"provider": "openai",
"model": "gpt-5.4-nano",
"timeout_ms": 10000,
"max_body_bytes": 32768,
"redact_before_ai": true,
"openai_api_key_env": "OPENAI_API_KEY"
}
}
The OpenAI API key is read from the configured environment variable and is not stored in the dashboard or config file. Sensitive headers, body samples, and query values are redacted before AI context is sent when redact_before_ai is enabled. Saved notes include the model, prompt hash, summary, and structured AI output, not the full prompt.
The threat scanner can inspect HTTP requests and responses with local heuristics and, when configured, ask OpenAI for a second opinion before blocking suspicious traffic.
$env:OPENAI_API_KEY = "sk-..."
On macOS/Linux:
export OPENAI_API_KEY="sk-..."
config.json:{
"threat_scanner": {
"enabled": true,
"mode": "suspicious_only",
"provider": "openai",
"model": "gpt-5.4-nano",
"second_opinion_model": "gpt-5.4-mini",
"scan_requests": true,
"scan_responses": true,
"max_body_bytes": 131072,
"max_ai_body_bytes": 32768,
"ai_timeout_ms": 750,
"block_threshold": 0.85,
"warn_threshold": 0.65,
"require_ai_confirmation_for_block": true,
"block_critical_local_on_ai_failure": true,
"fail_open": true,
"scan_content_types": [
"text/html",
"text/plain",
"application/json",
"application/javascript",
"text/javascript",
"application/xml"
],
"skip_content_types": [
"image/",
"video/",
"audio/",
"font/",
"application/octet-stream"
],
"trusted_domains": [
"accounts.google.com",
"login.microsoftonline.com",
"github.com"
],
"allowlist_domains": [],
"malicious_domains": [],
"malicious_file_hashes": [],
"threat_intel_updated": "",
"quarantine_dir": "quarantine",
"debug_log_path": "threats.log",
"redact_before_ai": true,
"store_bodies": false,
"openai_api_key_env": "OPENAI_API_KEY"
}
}
go run . --config ./config.json
The dashboard's Threat Scanner view shows scanned request/response counts, AI call counts, detections, verdict details, top local rules, and override actions.
Scanner modes:
suspicious_only: default; local heuristics decide when to call AI.all_text: calls AI for text-like traffic.paranoid: also calls AI for text-like traffic and is intended for high-sensitivity testing.metadata_only: uses headers, URL, host, and metadata without AI body review.off: disables scanning.Useful safety and privacy controls:
redact_before_ai: redacts common secrets and personal data before sending evidence to OpenAI.max_ai_body_bytes: limits the body sample included in AI evidence.require_ai_confirmation_for_block: prevents local heuristics from blocking unless AI confirms, except where block_critical_local_on_ai_failure is enabled for critical local evidence.fail_open: allows traffic when the scanner fails, unless stricter blocking settings apply.trusted_domains and allowlist_domains: reduce false positives for known-good hosts.malicious_domains and malicious_file_hashes: add local threat-intel hits without waiting for AI.debug_log_path: writes scanner decisions to a local JSONL-style log for debugging.To use a different environment variable name for the API key, set openai_api_key_env and export that variable before starting the proxy. Do not put API keys directly in config.json.
To intercept HTTPS, import and trust ca-cert.pem in your OS/browser:
Without trusting the CA, browsers will show certificate warnings for intercepted sites.
Set your HTTP/HTTPS proxy to the listen address (default http://localhost:8080).
Examples with curl:
# HTTP
curl -x http://localhost:8080 http://example.com/
# HTTPS (after trusting the CA for full MITM)
curl -x http://localhost:8080 https://example.com/
# Disable MITM and tunnel only
./mitm-proxy --mitm=false
# Change listen address
./mitm-proxy --listen=127.0.0.1:9090
WebSocket notes:
The cache is file‑based and only considers HTTP GET requests when enabled. Selection is controlled by:
On a cache hit, responses include:
Cache directory is ensured on startup and on config changes. If no directory is set, it defaults to ./cache.
go build ./
./mitm-proxy --config ./config.json
The server binds to the configured listen_addr and handles HTTP + HTTPS with ALPN.
Issues and pull requests are welcome. For significant changes, please open an issue first to discuss scope and design.
Coding style: keep changes minimal and focused; prefer clarity and small, composable functions.