mitmproxy Guide: Open-Source HTTP Traffic Interception
mitmproxy is a free, open-source interactive HTTPS proxy that gives developers and security researchers full control over HTTP and HTTPS traffic. Unlike GUI-based tools, mitmproxy is built for the command line and offers a powerful Python scripting API that lets you automate traffic analysis, modification, and testing at scale.
Whether you are debugging API integrations, testing application security, or building automated testing pipelines, mitmproxy provides the flexibility and programmability that commercial tools cannot match.
What Is mitmproxy?
mitmproxy stands for “man-in-the-middle proxy.” It intercepts HTTP and HTTPS traffic between a client and server, allowing you to inspect, modify, replay, and record all communication. The project includes three tools:
- mitmproxy — Interactive terminal-based UI for real-time traffic inspection
- mitmdump — Command-line tool for non-interactive traffic processing (like tcpdump for HTTP)
- mitmweb — Browser-based GUI for visual inspection
All three share the same core proxy engine and support the same scripting API.
Installation
Using pip (Recommended)
# Install via pip
pip install mitmproxy
# Verify installation
mitmproxy --versionUsing Homebrew (macOS)
brew install mitmproxyUsing Docker
docker run --rm -it -p 8080:8080 mitmproxy/mitmproxyPlatform-Specific Packages
# Ubuntu/Debian
sudo apt install mitmproxy
# Arch Linux
sudo pacman -S mitmproxyBasic Usage
Starting the Proxy
# Start interactive terminal UI on default port 8080
mitmproxy
# Start on a custom port
mitmproxy --listen-port 9090
# Start the web interface instead
mitmweb --web-port 8081
# Start non-interactive dump mode
mitmdump -p 8080Configuring Your Client
Point your browser or application to mitmproxy:
Proxy Host: 127.0.0.1
Proxy Port: 8080For system-wide proxying on macOS:
# Set HTTP proxy
networksetup -setwebproxy "Wi-Fi" 127.0.0.1 8080
# Set HTTPS proxy
networksetup -setsecurewebproxy "Wi-Fi" 127.0.0.1 8080Terminal UI Navigation
The mitmproxy terminal interface uses keyboard shortcuts:
| Key | Action |
|---|---|
j/k | Move down/up through flow list |
Enter | View flow details |
Tab | Switch between request/response |
q | Back / Quit |
f | Set filter expression |
i | Set intercept filter |
r | Replay flow |
e | Edit flow |
z | Clear flow list |
w | Save flows to file |
SSL/TLS Interception
To inspect HTTPS traffic, you need to install mitmproxy’s CA certificate on your device.
Installing the Certificate
- Start mitmproxy
- Configure your browser to use the proxy
- Visit
http://mitm.itin your browser - Download and install the certificate for your platform
# macOS: Trust the certificate
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain \
~/.mitmproxy/mitmproxy-ca-cert.pem
# Linux: Copy to trusted store
sudo cp ~/.mitmproxy/mitmproxy-ca-cert.pem /usr/local/share/ca-certificates/mitmproxy.crt
sudo update-ca-certificates
# For Python requests library
export REQUESTS_CA_BUNDLE=~/.mitmproxy/mitmproxy-ca-cert.pemMobile Device Setup
# iOS
# 1. Set device proxy to computer_ip:8080
# 2. Visit mitm.it in Safari
# 3. Install profile
# 4. Settings > General > About > Certificate Trust > Enable mitmproxy
# Android
# 1. Set Wi-Fi proxy to computer_ip:8080
# 2. Visit mitm.it in browser
# 3. Download and install certificatePython Scripting
The scripting API is mitmproxy’s most powerful feature. You can write Python scripts that hook into any stage of the request/response lifecycle.
Script Structure
# modify_requests.py
from mitmproxy import http
def request(flow: http.HTTPFlow) -> None:
"""Called when a request is received."""
# Add a custom header to all requests
flow.request.headers["X-Custom-Header"] = "mitmproxy"
def response(flow: http.HTTPFlow) -> None:
"""Called when a response is received."""
# Log response status codes
print(f"{flow.request.url} -> {flow.response.status_code}")Run the script:
mitmproxy -s modify_requests.py
# or
mitmdump -s modify_requests.pyPractical Script Examples
Block Specific Domains
from mitmproxy import http
BLOCKED_DOMAINS = ["ads.example.com", "tracker.example.com"]
def request(flow: http.HTTPFlow) -> None:
if any(domain in flow.request.host for domain in BLOCKED_DOMAINS):
flow.response = http.Response.make(
403,
b"Blocked by mitmproxy",
{"Content-Type": "text/plain"}
)Capture and Save API Responses
import json
from mitmproxy import http
def response(flow: http.HTTPFlow) -> None:
if "api.target.com" in flow.request.host:
data = {
"url": flow.request.url,
"method": flow.request.method,
"status": flow.response.status_code,
"headers": dict(flow.response.headers),
"body": flow.response.text[:5000],
}
with open("captured_api_calls.jsonl", "a") as f:
f.write(json.dumps(data) + "\n")Modify JSON Responses
import json
from mitmproxy import http
def response(flow: http.HTTPFlow) -> None:
if flow.request.url.endswith("/api/config"):
content_type = flow.response.headers.get("Content-Type", "")
if "application/json" in content_type:
data = json.loads(flow.response.text)
data["feature_flags"]["new_ui"] = True
data["debug_mode"] = True
flow.response.text = json.dumps(data)Request/Response Logging with Timing
import time
from mitmproxy import http
def request(flow: http.HTTPFlow) -> None:
flow.metadata["request_time"] = time.time()
def response(flow: http.HTTPFlow) -> None:
duration = time.time() - flow.metadata.get("request_time", time.time())
size = len(flow.response.content) if flow.response.content else 0
print(f"[{duration:.3f}s] [{size:>8} bytes] "
f"{flow.request.method} {flow.request.url} "
f"-> {flow.response.status_code}")Advanced Features
Transparent Proxying
Instead of configuring each client manually, you can set up mitmproxy as a transparent proxy using iptables:
# Enable IP forwarding
sudo sysctl -w net.ipv4.ip_forward=1
# Redirect HTTP traffic
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 \
-j REDIRECT --to-port 8080
# Redirect HTTPS traffic
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 \
-j REDIRECT --to-port 8080
# Start mitmproxy in transparent mode
mitmproxy --mode transparentReverse Proxy Mode
Place mitmproxy in front of your own server to inspect incoming traffic:
# Forward all traffic to your backend server
mitmproxy --mode reverse:https://api.yourserver.com --listen-port 443Upstream Proxy Mode
Chain mitmproxy with an external rotating proxy service:
# Route traffic through an upstream proxy
mitmproxy --mode upstream:http://proxy.example.com:8080 \
--upstream-auth username:passwordThis is particularly useful when debugging web scraping pipelines that use residential proxies or other proxy infrastructure.
Flow Filtering
mitmproxy supports powerful filter expressions:
# Only show flows matching a filter
mitmproxy --set view_filter="~d api.example.com"
# Filter examples
~d example.com # Domain contains
~u /api/v2 # URL contains
~m POST # Method is POST
~s "200" # Response status
~h "application/json" # Header contains
~b "error" # Body contains
~t "application/json" # Content-type
~c 200 # Status code equals
! ~d google.com # NOT domain
~d api.com & ~m POST # AND
~d api.com | ~d web.com # ORSaving and Loading Flows
# Save all flows to a file
mitmdump -w captured_flows.mitm
# Read and replay saved flows
mitmdump -r captured_flows.mitm
# Filter while reading
mitmdump -r captured_flows.mitm -n "~d api.example.com"
# Convert flows to HAR format
mitmdump -r captured_flows.mitm --set hardump=output.harmitmproxy for Web Scraping Development
mitmproxy is an excellent tool for developing and debugging web scraping solutions. Here is a workflow that combines mitmproxy with proxy infrastructure:
# scraping_debug_addon.py
"""
mitmproxy addon for debugging web scraping sessions.
Logs all requests, detects bot-detection responses,
and measures response times.
"""
import json
import time
from mitmproxy import http
class ScrapingDebugger:
def __init__(self):
self.stats = {
"total_requests": 0,
"blocked_responses": 0,
"captcha_pages": 0,
"successful": 0,
}
def request(self, flow: http.HTTPFlow):
self.stats["total_requests"] += 1
flow.metadata["start_time"] = time.time()
def response(self, flow: http.HTTPFlow):
elapsed = time.time() - flow.metadata.get("start_time", time.time())
body = flow.response.text.lower() if flow.response.text else ""
if flow.response.status_code == 403:
self.stats["blocked_responses"] += 1
print(f"BLOCKED: {flow.request.url}")
elif "captcha" in body or "challenge" in body:
self.stats["captcha_pages"] += 1
print(f"CAPTCHA: {flow.request.url}")
else:
self.stats["successful"] += 1
if self.stats["total_requests"] % 100 == 0:
print(f"\n=== Stats after {self.stats['total_requests']} requests ===")
print(json.dumps(self.stats, indent=2))
addons = [ScrapingDebugger()]Run with:
mitmdump -s scraping_debug_addon.py --mode upstream:http://your-proxy:8080mitmproxy vs Other Tools
| Feature | mitmproxy | Charles Proxy | Fiddler | Burp Suite |
|---|---|---|---|---|
| Price | Free/OSS | $50 | Free | $449/yr |
| Python Scripting | Full API | No | No | Jython |
| Terminal UI | Yes | No | No | No |
| Web UI | Yes | No | No | Yes |
| Transparent Mode | Yes | Limited | No | Yes |
| Performance | High | Medium | Medium | High |
| Automation | Excellent | Limited | .NET | Good |
For GUI-based debugging, see our Charles Proxy guide. For understanding proxy protocols, check our HTTP vs HTTPS proxies comparison.
FAQ
Is mitmproxy safe to use?
mitmproxy is safe when used on your own traffic for development and testing purposes. The tool itself is open-source and auditable. Installing the CA certificate does expose your machine to potential risks if the mitmproxy private key is compromised, so remove the certificate when not actively using it.
Can mitmproxy handle high traffic volumes?
Yes. mitmproxy is written in Python with asyncio and can handle thousands of concurrent connections. For very high volumes, use mitmdump (non-interactive mode) instead of the terminal UI to reduce overhead.
How do I use mitmproxy with certificate pinning?
Applications that implement certificate pinning will reject mitmproxy’s certificate. Options include: using Frida to bypass pinning at runtime, patching the application, or using a device with root access to install the certificate at the system level.
Can I use mitmproxy in CI/CD pipelines?
Absolutely. mitmdump is designed for non-interactive use and works well in CI/CD environments. Write Python scripts to validate API calls, check for security issues, or verify request patterns as part of your test suite.
Does mitmproxy support HTTP/2 and WebSockets?
Yes. mitmproxy supports HTTP/2, WebSocket connections, and can even handle gRPC traffic. Enable HTTP/2 with the --set http2=true flag if not enabled by default.
- Datacenter vs Residential Proxies: Complete Comparison
- Docker Proxy Setup: Configure Containers to Use Proxies
- Anti-Bot Detection Glossary: 50+ Terms Defined
- Anti-Bot Terminology Glossary: Complete A-Z Reference 2026
- Backconnect Proxies Deep Dive: Architecture and Real-World Performance
- Best Proxies in Southeast Asia: Singapore, Thailand, Indonesia, Philippines
- Datacenter vs Residential Proxies: Complete Comparison
- Docker Proxy Setup: Configure Containers to Use Proxies
- Anti-Bot Detection Glossary: 50+ Terms Defined
- Anti-Bot Terminology Glossary: Complete A-Z Reference 2026
- Backconnect Proxies Deep Dive: Architecture and Real-World Performance
- Best Proxies in Southeast Asia: Singapore, Thailand, Indonesia, Philippines
- Datacenter vs Residential Proxies: Complete Comparison
- Docker Proxy Setup: Configure Containers to Use Proxies
- 403 Forbidden Error: What It Means & How to Fix It
- 407 Proxy Authentication Required: Fix Guide
- Anti-Bot Detection Glossary: 50+ Terms Defined
- Anti-Bot Terminology Glossary: Complete A-Z Reference 2026
Related Reading
- Datacenter vs Residential Proxies: Complete Comparison
- Docker Proxy Setup: Configure Containers to Use Proxies
- 403 Forbidden Error: What It Means & How to Fix It
- 407 Proxy Authentication Required: Fix Guide
- Anti-Bot Detection Glossary: 50+ Terms Defined
- Anti-Bot Terminology Glossary: Complete A-Z Reference 2026