mitmproxy Guide: Open-Source HTTP Traffic Interception

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 --version

Using Homebrew (macOS)

brew install mitmproxy

Using Docker

docker run --rm -it -p 8080:8080 mitmproxy/mitmproxy

Platform-Specific Packages

# Ubuntu/Debian
sudo apt install mitmproxy

# Arch Linux
sudo pacman -S mitmproxy

Basic 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 8080

Configuring Your Client

Point your browser or application to mitmproxy:

Proxy Host: 127.0.0.1
Proxy Port: 8080

For 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 8080

Terminal UI Navigation

The mitmproxy terminal interface uses keyboard shortcuts:

KeyAction
j/kMove down/up through flow list
EnterView flow details
TabSwitch between request/response
qBack / Quit
fSet filter expression
iSet intercept filter
rReplay flow
eEdit flow
zClear flow list
wSave flows to file

SSL/TLS Interception

To inspect HTTPS traffic, you need to install mitmproxy’s CA certificate on your device.

Installing the Certificate

  1. Start mitmproxy
  2. Configure your browser to use the proxy
  3. Visit http://mitm.it in your browser
  4. 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.pem

Mobile 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 certificate

Python 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.py

Practical 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 transparent

Reverse 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 443

Upstream 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:password

This 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 # OR

Saving 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.har

mitmproxy 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:8080

mitmproxy vs Other Tools

FeaturemitmproxyCharles ProxyFiddlerBurp Suite
PriceFree/OSS$50Free$449/yr
Python ScriptingFull APINoNoJython
Terminal UIYesNoNoNo
Web UIYesNoNoYes
Transparent ModeYesLimitedNoYes
PerformanceHighMediumMediumHigh
AutomationExcellentLimited.NETGood

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.


Related Reading

Scroll to Top