Reading Flows
A capture you cannot read back is just a rumour. This chapter opens the flow file from chapter 4, lists what is inside, and takes one flow apart until there is nothing left in it you cannot name.
Reading a flow file
You do not need the proxy running to read
a recording. -r reads instead of
listening, and -n says no server at all:
mitmdump -nr lab-http.mitm
(transcript t02):
127.0.0.1:59850: GET http://127.0.0.1:18080/hello
<< 200 OK 164b
127.0.0.1:59866: GET http://127.0.0.1:18080/search?q=teapot&page=2
<< 200 OK 181b
127.0.0.1:59878: POST http://127.0.0.1:18080/plants
<< 200 OK 257b
127.0.0.1:59888: GET http://127.0.0.1:18080/missing
<< 404 Not Found 166b
One flow takes two lines in this summary
view: the request, then << and the
response's status and body size. The
number before the colon on the left is the
client's own port for that connection — how
you tell two otherwise identical requests
apart.
One flow, in full
Raise the detail with the flow_detail
option — 1 is the summary above, 4 is
everything mitmdump knows how to print:
mitmdump --set flow_detail=4 -nr lab-http.mitm "~u /plants"
(the ~u /plants part is a filter —
chapter 6 — picking the one flow whose
URL matches). The lab's output, trimmed of
nothing that matters (transcript t02):
127.0.0.1:59878: POST http://127.0.0.1:18080/plants
Host: 127.0.0.1:18080
User-Agent: curl/8.5.0
Accept: */*
Proxy-Connection: Keep-Alive
Content-Length: 20
Content-Type: application/x-www-form-urlencoded
name: fern
soil: moist
<< 200 OK 257b
Server: BaseHTTP/0.6 Python/3.12.3
Date: Sun, 25 Oct 2026 02:23:14 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 257
served-by: plain-echo-server
method: POST
path: /plants
...
body:
name=fern&soil=moist
Walk it once, top to bottom:
- Request line. Method, full URL, and later (in the console and web views) the timing: when the request arrived, how long the server took.
- Request headers. Indented under the
request: who the client claims to be
(
User-Agent), what it accepts, and the giveaway from chapter 4,Proxy-Connection, present because curl knew it was talking to a proxy. - Request body. The form data,
decoded by mitmdump:
name: fern,soil: moist. On the wire this wasname=fern&soil=moist, as the echo server's own report at the bottom shows. Decoding is a courtesy the tool extends to form posts, JSON, and several other common shapes — and a thing to remember: you are reading an interpretation, and for an argument about exact bytes, the raw body is one view away in the console and web faces. - Response line.
<<, the status code and its name, and the body size. - Response headers and body. The server's side of the story — here, the echo server's report, which is why this flow contains the request twice: once as mitmproxy recorded it, and once as the server described receiving it. When the two disagree someday — a header present in one, missing in the other — you will have found something real: something between the seats changed the traffic. Chapter 9 will make that something be you.
What else a flow holds
The printed views are summaries of a
richer object. A flow also records the
client and server addresses, the TLS
details of both connections when there are
any (chapter 8), redirects already
followed, WebSocket messages when the
conversation upgrades, and any error —
a flow can end in failure, and the file
keeps failures too, marked as errors
rather than responses. The console and
mitmweb (chapter 7) show all of it in
panels; mitmdump's flow_detail shows
most. An addon (chapter 9) sees all of
it, as Python objects, while it is live.
Keeping recordings
A flow file keeps everything, including things people typed into forms — in the lab, a fern's soil preference; in real browsing, passwords, session cookies, personal messages. Treat flow files the way chapter 3 treats the CA folder: they are evidence about you, possibly about anyone whose traffic you were permitted to record, and they deserve the same handling as the data they contain. Save them where you would save the secrets in them, delete them when the question is answered, and never attach one to a public bug report without reading every flow in it first. Chapter 6's filters double as the redaction tool: read a file in, keep only the flows that match, write those out, and share the smaller file.