How to Debug HTTP Traffic: Inspect Requests and Replay Failures
To debug HTTP traffic, identify the failing request, inspect its response, and reproduce the behavior in a controlled environment. The right capture tool depends on where that request originates and what you need to do with it.
| Your task | Start here | What you can inspect or reuse |
|---|---|---|
| Explain a request made by the browser tab you have open | Browser DevTools Network panel | URL, method, headers, payload, response, initiator and timing |
| Intercept and change requests while debugging a client | An interactive HTTP proxy | Requests and responses passing through the configured interception path |
| Record HTTP reaching a server and send those requests to another backend | GoReplay | A captured HTTP request stream, optionally saved for later replay |
| Diagnose packet loss, TCP behavior or network framing | A packet analyzer | Network packets; a different artifact from an application replay test |
Chrome’s Network documentation explains browser inspection. HTTP Toolkit’s getting-started guide describes its intercept, inspect and rewrite workflow. GoReplay is useful when you need server-side requests that you can replay against a target; it does not provide a recording of the browser screen.
Reproduce an HTTP failure locally
Start with a deliberately small failure: a price endpoint accepts currency=USD but rejects currency=INVALID. A 422 response is the expected validation result in this example, not a network failure.
- In a browser or proxy, inspect the failing request’s query parameter, status and response body. Compare it with a successful request. A matching path does not mean the payload or authentication context matches.
- Reproduce those two requests against an isolated target.
- Check the application result, including the expected rejection. Do not judge the run only by whether GoReplay sent requests successfully.
The local replay exercise makes this sequence repeatable without production data. With Python 3.9+ and an installed GoReplay binary, save the script as replay-demo.py, inspect it, then run:
python3 replay-demo.py --gor /path/to/gor
The script creates three synthetic records in a temporary directory, filters the file to GET requests, writes one requests.gor file using append mode, and replays it to a temporary server bound to 127.0.0.1. Its assertions expect:
| Synthetic input | Expected target observation |
|---|---|
GET /price?currency=USD | 200 |
GET /price?currency=INVALID | 422 |
POST /do-not-replay | Absent after GET filtering |
A successful exercise prints JSON with result: "passed", two replayed requests and one filtered POST. A missing binary or an unexpected target observation makes it fail. The script closes its server and removes its temporary files when finished.
This is synthetic file filtering and local HTTP replay. It does not capture production packets, test TLS, find a root cause automatically or compare arbitrary application responses. The assertions belong to this sample application. For a real defect, define what the correct response and resulting state should be before replaying it.
Capture so you can replay
For server-side capture, first choose a service and traffic scope you are authorized to inspect. GoReplay needs access to the interface carrying the service’s plaintext HTTP. Raw packet capture generally requires root or appropriate capabilities; consult the capture guide for the deployment and capture engine.
For HTTPS, capture the HTTP leg after TLS termination, if one exists. If the upstream connection is also encrypted, that port is not a plaintext capture point. --input-raw does not decrypt TLS. Replaying to an https:// destination is a separate operation; see HTTPS capture and replay.
The example below creates a fresh directory and captures up to 30 seconds of GET traffic from port 8080:
capture_dir=$(mktemp -d)
sudo gor --input-raw :8080 --http-allow-method GET \
--output-file "$capture_dir/requests.gor" --output-file-append \
--exit-after 30s
Run it while the selected service receives the requests you want to inspect. --output-file-append keeps the filename consistent with the replay command; default file output uses numbered chunks. Start a new sample in a fresh directory rather than appending unrelated traffic to an old capture. See file output.
A GET filter limits methods. It does not remove secrets or guarantee that an endpoint has no side effects. For real traffic, define the required filters, request rewrites or middleware before saving or forwarding sensitive values. Review the resulting artifact.
With an isolated replay target already running, replace this example hostname with that target and replay from the same shell:
gor --input-file "$capture_dir/requests.gor" \
--output-http="http://staging.example.com" --exit-after 60s
GoReplay sends the recorded requests to that target. The 60-second limit bounds this example; it can truncate a longer capture or leave responses unfinished. Choose a limit that covers the sample’s recorded duration and response time, then check the expected request count rather than assuming the entire file was replayed. Verify the target’s request log, status, response body and relevant state change. The capture command above records requests; it does not automatically save the origin responses for comparison. The replay guide covers target Host handling and output options.
Troubleshooting Common Capture Issues
| Symptom | Check next |
|---|---|
| Empty capture | Service port, network namespace/interface, capture permissions, whether traffic arrived, and whether the selected leg is encrypted |
| A suffixed file exists but the named replay file does not | Default chunk naming; use the actual file set, or a fresh append-mode capture as above |
| Target receives nothing | Input file contents, filters, target reachability and GoReplay errors |
Replay returns 401 or 403 | Expired tokens, target-specific credentials, cookies and authorization rules; do not reuse production credentials by default |
Replay returns 404 | Path, destination scheme and Host behavior; see request rewriting |
A 200 response contains the wrong result | Add application assertions; transport success is not semantic correctness |
| The same request behaves differently | Target data, dependency responses, feature flags, time and stateful request ordering |
For origin-versus-candidate analysis, enable the appropriate origin/replay response tracking and correlate asynchronous messages by request ID. Build comparison rules for your application, including intentional differences such as timestamps. The middleware protocol describes the message types and tracking flags.
Keep the reproduction useful
Retain the smallest request set that demonstrates the failure, the target version and configuration, the expected outcome, and the actual observation. Protect the captured file and give it a retention deadline. Isolate databases, queues, webhooks, payment and email effects before sending copied traffic to a target.
Once you can reproduce one request, use record and replay testing to turn the example into a repeatable check. Use shadow testing when the question is how a candidate behaves under a sampled live workload. Neither step replaces tests for behavior absent from the capture.