| name | mitmdump-windows |
| description | Cheatsheet for mitmdump — the mitmproxy command-line tool — on Windows to intercept, capture, inspect, modify, replay, filter, and analyze HTTP/HTTPS/TCP/WebSocket/DNS traffic. Use it for essentially any mitmdump/mitmproxy CLI task: standing up a MITM/HTTP(S) proxy, decrypting HTTPS by trusting the mitmproxy CA with `certutil`, saving/reading `.mitm` flow files, exporting HAR or curl, on-the-fly request/response tampering (map_local, map_remote, modify-headers/body), client/server replay, scripting with `-s` addons, and security analysis of web / mobile / IoT / malware-C2 traffic. Trigger on phrasings like "intercept HTTPS", "capture app traffic", "MITM proxy on Windows", "set up mitmdump", "analyze API traffic", "decrypt TLS for Wireshark", "mitmproxy addon/script", or "install the mitmproxy certificate". Assumes mitmdump is installed and on PATH. Windows-only (PowerShell/cmd); targets mitmdump, not the mitmproxy TUI or mitmweb GUI. |
mitmdump on Windows — Cheatsheet
A dense command reference for mitmdump, the scriptable, non-interactive member of the mitmproxy
family (the other two — mitmproxy TUI and mitmweb GUI — take the same flags). This is a
cheatsheet, not a fixed procedure: jump to the section you need and copy the snippet.
Install the dependency
Install the official Windows package, open a new terminal, and verify it:
winget install --id mitmproxy.mitmproxy --exact
mitmdump --version
Assumes mitmdump is already installed and on PATH (verified default install:
C:\Program Files\mitmproxy\bin\mitmdump.exe). All examples are written for Windows PowerShell;
cmd.exe variants are noted where they differ.
- Config dir:
%USERPROFILE%\.mitmproxy (CA certs, config.yaml, generated leaf certs). Override
with --set confdir=DIR.
- Default proxy: regular HTTP(S) proxy on
127.0.0.1:8080. Point your client/app there.
- If
mitmdump is not on PATH, call it directly with the PowerShell call operator (the path has a
space): & "C:\Program Files\mitmproxy\bin\mitmdump.exe" --version.
The deeper upstream docs are copied verbatim under reference/ — read them when the
cheatsheet isn't enough: certificates.md, filters.md,
modes.md, features.md.
Verified against mitmdump 12.2.3 (Python 3.14, OpenSSL 3.5) on Windows 11. Every flag, filter,
command, and one-liner below was confirmed against the installed binary's --help/--options/--commands.
1. First-time setup — trust the CA so HTTPS decrypts
HTTPS interception only works if the client trusts mitmproxy's CA. The CA is generated on first run
into %USERPROFILE%\.mitmproxy. If that folder is empty, run mitmdump once and Ctrl+C to create it.
Use mitmproxy-ca-cert.cer (the public cert) for trust on Windows. It goes in the Trusted Root
Certification Authorities ("root") store. Do not import the .p12 files for trust —
mitmproxy-ca.p12 contains the private key.
# A) Current-user trust — NO admin needed (recommended for a workstation)
certutil -user -addstore root "$env:USERPROFILE\.mitmproxy\mitmproxy-ca-cert.cer"
# B) Machine-wide trust (all users) — must run in an ELEVATED (Administrator) terminal
# This is the official one-liner from https://docs.mitmproxy.org/stable/concepts/certificates/#quick-setup
certutil -addstore root "$env:USERPROFILE\.mitmproxy\mitmproxy-ca-cert.cer"
Add -f to overwrite without prompts. The classic doc form is certutil -addstore root mitmproxy-ca-cert.cer run from inside %USERPROFILE%\.mitmproxy.
Verify / list / remove:
certutil -user -store root mitmproxy # show the cert (serial + SHA-1 thumbprint)
certutil -user -verifystore root mitmproxy # validate chain/validity
certutil -user -delstore root mitmproxy # remove it (use plain form, elevated, for machine store)
# PowerShell-native equivalents
Get-ChildItem Cert:\CurrentUser\Root | Where-Object Subject -match 'mitmproxy'
Get-ChildItem Cert:\CurrentUser\Root | Where-Object Subject -match 'mitmproxy' | Remove-Item
Quick install for any device on your LAN: start mitmdump, point the device's proxy at your
machine, then browse to http://mitm.it — it serves the right cert + per-platform install steps.
Cert pinning: apps that pin certs reject mitmproxy's CA. mitmdump alone can't defeat a pin — either
pass the host through with --ignore-hosts (§9) or unpin the app with Frida/objection/apk-mitm. See
certificates.md.
2. How to drive mitmdump (the core patterns)
mitmdump is non-interactive: it starts a proxy, prints/saves flows, and runs until Ctrl+C — unless
you give it -n and -r to batch-process a file and exit.
mitmdump # regular proxy on :8080, print flows to console
mitmdump -p 9090 # listen on a different port
mitmdump -q # quiet: suppress startup/log chatter
mitmdump -w out.mitm # capture: stream flows to a file as they arrive
mitmdump -n -r out.mitm # offline: read a saved file, no proxy server, then exit
mitmdump --version / --help / --options / --commands # self-documentation
| Flag | Meaning |
|---|
-p, --listen-port PORT | Listen port (default 8080). |
--listen-host HOST | Bind address. --listen-host 0.0.0.0 to accept other devices. |
-m, --mode MODE | Proxy mode (§4). Repeatable. |
-w, --save-stream-file PATH | Stream flows to file. Prefix + to append; path supports strftime. |
-r, --rfile PATH | Read flows from a saved file. |
-n, --no-server | Don't start the proxy listener (pair with -r for pure batch processing). |
-s, --scripts SCRIPT | Load an addon/script (§11). Repeatable; live-reloads on save. |
-q / -v | Quiet / more verbose logging. |
--flow-detail LEVEL | Per-flow console verbosity, 0–4 (table below). Default 1. |
--set opt[=value] | Set any option (mitmdump --options lists them all). |
[filter] (trailing arg) | A filter expression (§5) that sets both view_filter and save_stream_filter. |
--flow-detail levels: 0 nothing · 1 short URL + status (default) · 2 full URL + status +
headers · 3 += truncated bodies / WS+TCP messages · 4 = everything, untruncated.
3. Filter expressions (the heart of mitmdump)
Filters are used everywhere: the trailing [filter] arg, --stickycookie/--stickyauth, the
block_list option, and the [/flow-filter] part of --modify-headers/--modify-body/--map-*.
| Op | Match | Op | Match |
|---|
~q | request w/ no response | ~s | response |
~m regex | method | ~u regex | URL |
~d regex | domain | ~c int | response status code |
~h regex | header (name: value) | ~hq / ~hs | request / response header |
~b regex | body | ~bq / ~bs | request / response body |
~t regex | Content-Type | ~tq / ~ts | request / response Content-Type |
~a | asset (css/js/img/font) | ~e | error flow |
~src regex | client address | ~dst regex | destination address |
~http ~tcp ~udp ~dns ~websocket | by protocol | ~all | every flow |
~marked | marked flows | ~marker regex | flows with a specific marker |
~comment regex | flow comment | ~meta regex | flow metadata |
~replay | replayed flows | ~replayq / ~replays | replayed request / response |
Combinators: ! not · & and · | or · ( ) grouping. The default operator is & — placing
expressions side by side ANDs them (~q ~m POST ≡ ~q & ~m POST). A bare regex with no operator
matches the URL (≡ ~u). Regexes are Python-style and case-insensitive by default
(set MITMPROXY_CASE_SENSITIVE_FILTERS=1 to change).
PowerShell quoting: wrap the whole filter in single quotes so &, |, (, ) aren't
eaten by the shell: mitmdump '~m POST & ~d ^api\.'.
mitmdump '~m POST & ~d ^api\.' # POSTs to api.* hosts
mitmdump '~e | ~c ^5\d\d$' # errors or 5xx responses
mitmdump -w auth.mitm '~hq ^Authorization:' # capture only requests bearing an Authorization header
mitmdump '~d example\.com & ~ts application/json & !~a' # JSON from example.com, skipping static assets
The @all/@focus/@shown/@marked/... selectors are interactive-only (TUI/mitmweb) and do not
work as a mitmdump [filter] arg. Full operator notes: filters.md.
4. Proxy modes
--mode is repeatable; append @port or @host:port to override the listener for that mode.
| Mode | Invocation | Use |
|---|
| regular (default) | mitmdump | Client configured to use an HTTP(S) proxy. Most robust. |
| local | mitmdump --mode local | Transparently capture apps on this Windows box — no proxy/route config. |
| reverse | mitmdump --mode reverse:https://example.com | Sit in front of a server; clients hit mitmdump. |
| upstream | mitmdump --mode upstream:http://proxy:8081 | Chain through another HTTP(S) proxy. |
| socks5 | mitmdump --mode socks5 | Act as a SOCKS5 proxy (for tools that only speak SOCKS). |
| dns | mitmdump --mode dns | Scriptable DNS server (@5353 for a custom port). |
| transparent / tun | Linux/macOS only | Not supported natively on Windows — use local instead, or route a victim VM through a Linux gateway. |
Local capture (Windows-native, no client config):
mitmdump --mode local # capture everything on this machine
mitmdump --mode local:firefox.exe # only this process (name, with/without .exe)
mitmdump --mode local:42 # only PID 42
mitmdump --mode "local:!chrome.exe" # everything EXCEPT chrome (quote ! and , in PowerShell)
mitmdump --mode "local:chrome.exe,msedge.exe"
Local mode handles redirection transparently (no proxy setting needed), but HTTPS still needs the CA
trusted (§1). Reverse mode can also tunnel raw protocols — --mode reverse:tcp://host:port,
tls://, dns://, quic://, etc. Full details: modes.md.
5. Capture & save flows
mitmdump -w out.mitm # write everything (native binary flow format)
mitmdump -w +out.mitm # APPEND to an existing file (note the +)
mitmdump -w "caps\%Y-%m-%d.mitm" # strftime in the path → one file per day
mitmdump -w posts.mitm '~m POST' # trailing filter also limits what is SAVED
mitmdump -w api.mitm --set save_stream_filter='~d api.example.com' # save-only filter (view unaffected)
mitmdump --listen-host 0.0.0.0 -p 8080 -w mobile.mitm # accept a phone/IoT device, capture
%Y/%H etc. are literal in both PowerShell and cmd (no shell expansion), so the same path string
works in both.
6. Read & process saved flows offline (-n -r)
-n (no server) + -r (read file) turns mitmdump into a batch transformer: read → filter → re-write →
exit. The trailing filter sets save_stream_filter, so only matching flows are written out.
mitmdump -n -r in.mitm --flow-detail 1 # print one line per flow
mitmdump -n -r in.mitm --flow-detail 2 '~d api.example.com' # print headers, only for one domain
mitmdump -n -r in.mitm -w errors.mitm '~c ^5\d\d$' # extract 5xx flows into a new file
mitmdump -n -r in.mitm -w keep.mitm '~d example\.com' # keep only one domain
mitmdump -n -r a.mitm -w +all.mitm; mitmdump -n -r b.mitm -w +all.mitm # merge captures (append)
7. Export — HAR, curl/httpie, CSV
HAR — there is no --har flag and -w file.har does not make HAR; use the hardump option:
mitmdump --set hardump=session.har # live: dump HAR of all flows on exit
mitmdump -n -r in.mitm --set hardump=out.har # convert a saved .mitm → HAR
mitmdump -n -r in.mitm --set hardump=api.har --set save_stream_filter='~d api.example.com' # filtered HAR
curl / httpie / raw — these are export commands (formats: curl, httpie, raw,
raw_request, raw_response), reached from a tiny script. Save each response as a runnable curl cmd:
import itertools, os
from mitmproxy import ctx
os.makedirs("curl", exist_ok=True)
c = itertools.count()
def response(flow):
path = os.path.join("curl", f"req_{next(c)}.txt")
ctx.master.commands.call("export.file", "curl", flow, path)
CSV of chosen fields (method/URL/status) for diffing two runs:
import csv
f = open("report.csv", "w", newline="", encoding="utf-8")
w = csv.writer(f); w.writerow(["method", "url", "status"])
def response(flow):
w.writerow([flow.request.method, flow.request.pretty_url, flow.response.status_code])
def done():
f.close()
8. Modify traffic on the fly (no script needed)
These are CLI flags; the pattern separator is the first character (here / or |), and
[/flow-filter] is an optional §5 filter.
| Flag | Pattern | Example |
|---|
-H, --modify-headers | /[filter]/name/value | -H '/~q/User-Agent/audit-scanner' (empty value removes the header) |
-B, --modify-body | /[filter]/regex/[@]replace | -B '/~s/"premium":\s*false/"premium": true' (@file reads replacement from a file) |
--map-local | |[filter]|url-regex|local-path | --map-local '|example\.com/app\.js|C:\test\app.js' |
-M, --map-remote | |[filter]|url-regex|replacement | -M '|//api\.example\.com|//staging.example.com' |
--anticache | — | Strip If-None-Match/If-Modified-Since to force full responses. |
--anticomp | — | Ask servers for uncompressed bodies (readable in capture). |
# Inject spoofed trust headers to probe IP-allowlist / path-confusion bypasses
mitmdump -H '/~q/X-Forwarded-For/127.0.0.1' -H '/~q/X-Original-URL/admin'
# Swap a live script for your instrumented copy (client-side testing)
mitmdump --map-local '|example\.com/app\.js|C:\pentest\app.patched.js'
# Strip a server-info-leaking header from every response
mitmdump -H '/~s/Server/'
See features.md for the full pattern grammar (capture groups, directory
mapping, @file replacements).
9. Scope, block, replay
# Scope: ignore (pass through untouched) vs allow (intercept only these)
mitmdump --ignore-hosts '(^|\.)pinned-api\.example\.com$' # pass pinned hosts through (pin stays intact)
mitmdump --allow-hosts '(^|\.)example\.com$' # intercept ONLY the target, ignore the rest
# Block: return a fixed status (or 444 = hang up, no response) for matching flows
mitmdump --set block_list=':~d google-analytics\.com:404' # 404 analytics
mitmdump --set block_list=':!~d ^example\.com$:403' # allow only example.com (403 everything else)
# Replay (use a saved file)
mitmdump -C requests.mitm # CLIENT replay: re-send the recorded requests to the live server
mitmdump -S responses.mitm # SERVER replay: answer matching requests from the file (offline mock)
mitmdump -S responses.mitm --server-replay-extra=404 # how to answer requests with no saved match
--ignore-hosts matches against host:port; in transparent/local capture prefer an IP/SNI. For TLS
upstream problems add -k (see §12). block_list/map_local are the quiet way to neutralize
telemetry without leaving the proxy.
10. Scripting with -s (addons)
Run mitmdump -s script.py. Scripts live-reload on save. Two shapes: a class in an addons list,
or just module-level hook functions (abbreviated syntax). Use Python logging for output (it routes to
mitmproxy's log). Key hooks: request(flow), response(flow), requestheaders/responseheaders,
error(flow), websocket_message(flow), tcp_message(flow), load, configure, done() (flush on
shutdown).
import logging
def request(flow):
flow.request.headers["X-Audit"] = "1"
def response(flow):
logging.info(f"{flow.response.status_code} {flow.request.pretty_url}")
Print a URL/host/User-Agent IOC list (recon / malware triage):
def request(flow):
ua = flow.request.headers.get("User-Agent", "")
print(f"{flow.request.host}\t{flow.request.pretty_url}\t{ua}")
Save only flows whose response body matches a secret-ish regex into a new flow file:
import re, logging
from mitmproxy import io
PAT = re.compile(rb"(api[_-]?key|secret|BEGIN PRIVATE KEY|password)", re.I)
fh = open("hits.mitm", "wb"); writer = io.FlowWriter(fh)
def response(flow):
if PAT.search(flow.response.raw_content or b""):
logging.warning(f"MATCH {flow.request.pretty_url}")
writer.add(flow)
def done():
fh.close()
Use a §5 filter inside a script (so you reuse the filter language):
from mitmproxy import flowfilter
flt = flowfilter.parse("~m POST & ~d example.com")
def response(flow):
if flowfilter.match(flt, flow):
...
For simple URL→host rewrites use flow.request.host = "..." (honor flow.request.pretty_host in
transparent/local mode). To stop everything from a script: from mitmproxy import ctx; ctx.master.shutdown().
11. Security analysis recipes
Concrete starting points for common engagements. Combine with §1 (trust CA) and a proxy pointing at
mitmdump (§13). Filters (§5) do the heavy lifting; nothing here uses an unverified flag.
Web app / API pentest & bug bounty
mitmdump -p 8080 -w target.mitm '~d (^|\.)example\.com$' # scoped capture
mitmdump -n -r target.mitm '~hq [Aa]uthorization|[Bb]earer|[Aa]pi[-_]?[Kk]ey' --flow-detail 2 # find auth/tokens
mitmdump -n -r target.mitm '~u (admin|debug|internal|graphql|/api/)' --flow-detail 1 # interesting endpoints
mitmdump -n -r target.mitm '~bs (password|secret|token|BEGIN PRIVATE KEY)' --flow-detail 3 # secrets in responses
mitmdump -p 8080 -B '/~s/"isAdmin":\s*false/"isAdmin": true' # flip a server-sent flag to unlock gated UI
Mobile app / thick-client / IoT
mitmdump --listen-host 0.0.0.0 -p 8080 -w app.mitm # device → your-PC-IP:8080, capture
mitmdump --listen-host 0.0.0.0 -p 8080 --allow-hosts '(^|\.)example\.com$' -w app.mitm # cut the noise
mitmdump -p 8080 --ignore-hosts '(^|\.)pinned\.example\.com$' # let a pinned endpoint through
mitmdump -n -r app.mitm '~bq (token|secret|api[_-]?key|password)' --flow-detail 3 # hardcoded creds
Malware / C2 traffic analysis (in an isolated detonation VM)
mitmdump -p 8080 -q -w "C:\malware\%Y%m%d-%H%M%S.mitm" # unattended timestamped capture
mitmdump --mode reverse:https://c2.example.com -p 8443 -q -w c2.mitm # impersonate a known C2 (redirect its domain to you first)
$env:SSLKEYLOGFILE = "C:\malware\sslkeys.log"; mitmdump -p 8080 -q -w m.mitm # log TLS keys for Wireshark
mitmdump -n -r c2.mitm -s iocs.py -q # extract host/URL/UA IOCs (script in §10)
API forensics / regression
mitmdump -n -r run-a.mitm --set hardump=a.har; mitmdump -n -r run-b.mitm --set hardump=b.har # HAR diff two runs
mitmdump -S golden.mitm --server-replay-extra=404 # serve a frozen API snapshot to a client
SSLKEYLOGFILE (or MITMPROXY_SSLKEYLOGFILE, which won't affect other apps) lets Wireshark decrypt a
parallel pcap: set it in Wireshark under Edit → Preferences → Protocols → TLS → (Pre)-Master-Secret
log filename.
12. Custom certs & mTLS (quick pointers)
mitmdump --certs "*=C:\path\leaf.pem" # use your own leaf cert (PEM: key + cert chain) for all domains
mitmdump --certs "*.example.com=C:\path\leaf.pem" # ...only for matching domains
mitmdump --set confdir=C:\my-ca # use a custom CA dir (drop your mitmproxy-ca.pem there)
mitmdump -k # --ssl-insecure: skip UPSTREAM cert verification (lab only — see warning)
mitmdump --set client_certs=C:\certs # present client certs to upstream (mTLS), by hostname or single file
-k makes mitmproxy itself vulnerable to interception — only in a controlled test. Full cert/mTLS
details: certificates.md.
13. Windows system proxy + opsec
Windows has two proxy configs: WinINET (browsers/user apps, per-user registry) and WinHTTP
(services, machine-level via netsh).
# WinINET (browsers / most apps) — PowerShell
$reg = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings'
Set-ItemProperty $reg ProxyServer '127.0.0.1:8080'; Set-ItemProperty $reg ProxyEnable 1 -Type DWord
# undo:
Set-ItemProperty $reg ProxyEnable 0 -Type DWord; Remove-ItemProperty $reg ProxyServer -ErrorAction SilentlyContinue
# WinHTTP (system services) — elevated cmd/PowerShell
netsh winhttp set proxy 127.0.0.1:8080
netsh winhttp reset proxy # undo
netsh winhttp show proxy # inspect
Restart the target app after changing WinINET (it re-reads on launch). WinHTTP doesn't support SOCKS.
Opsec / safety
- An open proxy is a risk.
--listen-host 0.0.0.0 exposes mitmdump as a relay on your network —
protect it: mitmdump --proxyauth username:password (or @htpasswd, or any to accept any creds).
- Authorize your testing. Capturing/decrypting third-party TLS without consent is illegal in most
places — keep it to systems and traffic you're authorized to inspect.
- Clean up when done: remove the trusted CA (§1), reset the system proxy (above), and unset
SSLKEYLOGFILE.
When the cheatsheet runs out
mitmdump --options (every option + default + doc), mitmdump --commands (every command +
signature), mitmdump --help — the authoritative, version-correct reference on this machine.
- Bundled upstream docs:
reference/. Online: https://docs.mitmproxy.org/stable/.