Python
HTTP requests
debugging
programming
web development

How can I see the entire HTTP request that's being sent by my Python application?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

To see the full HTTP request sent by a Python application, you need to decide what "full" means in your situation. Sometimes you only need the method, URL, headers, and body before the request is sent. Other times you need to inspect the actual wire traffic, which may require a proxy because HTTPS encrypts the request on the network.

Inspect the Request Before Sending with requests

If you are using the requests library, the easiest way to inspect a request is to build a PreparedRequest. That lets you see the exact method, URL, headers, and body that requests will send.

python
1import requests
2
3session = requests.Session()
4request = requests.Request(
5    method="POST",
6    url="https://httpbin.org/post",
7    headers={"X-Debug": "true"},
8    json={"name": "Ada"}
9)
10prepared = session.prepare_request(request)
11
12print(prepared.method)
13print(prepared.url)
14print(prepared.headers)
15print(prepared.body)

This is often the fastest way to answer questions such as:

  • did my code send JSON or form data
  • what headers were attached
  • did authentication get added
  • which URL was finally used

Printing the Request in a Readable Format

A helper function makes the output easier to scan:

python
1def dump_prepared_request(prepared):
2    print(f"{prepared.method} {prepared.url} HTTP/1.1")
3    for key, value in prepared.headers.items():
4        print(f"{key}: {value}")
5    print()
6    if prepared.body:
7        if isinstance(prepared.body, bytes):
8            print(prepared.body.decode("utf-8", errors="replace"))
9        else:
10            print(prepared.body)
11
12
13dump_prepared_request(prepared)

That gets you very close to the HTTP request structure you would expect to see in a debugger.

Inspecting a Sent Request from the Response Object

After requests sends the call, the response keeps a reference to the request object that was actually used:

python
1response = session.send(prepared)
2
3print(response.request.method)
4print(response.request.url)
5print(response.request.headers)
6print(response.request.body)

This is useful when redirects, session headers, or authentication hooks may have changed the request during sending.

Enable Library-Level Debug Logging

If you want more detail from the HTTP stack, you can enable logging from urllib3 and http.client. This is helpful when you need to trace connection behavior or raw request lines.

python
1import logging
2import http.client
3
4http.client.HTTPConnection.debuglevel = 1
5logging.basicConfig(level=logging.DEBUG)
6logging.getLogger("urllib3").setLevel(logging.DEBUG)
7logging.getLogger("urllib3").propagate = True

Then make the request normally:

python
import requests

requests.get("https://httpbin.org/get")

This will emit verbose connection and protocol output to the console. It is not always beautifully formatted, but it is often enough to verify what was transmitted.

Use a Proxy for Real Traffic Inspection

If you want to inspect the actual outbound HTTP exchange, especially with HTTPS, a debugging proxy is usually the right tool. Popular choices include:

  • 'mitmproxy'
  • Charles Proxy
  • Fiddler
  • Proxyman

With requests, you can direct traffic through a proxy like this:

python
1import requests
2
3proxies = {
4    "http": "http://127.0.0.1:8080",
5    "https": "http://127.0.0.1:8080",
6}
7
8requests.get("https://httpbin.org/get", proxies=proxies, verify=False)

Once the proxy is running, you can inspect the full request and response interactively.

For HTTPS, proper certificate setup is usually required if you want clean interception without disabling verification. Disabling verification is acceptable for local debugging only, not for normal application behavior.

Why HTTPS Changes the Debugging Story

With plain HTTP, packet capture tools can show the request directly. With HTTPS, the payload is encrypted on the wire. That means a packet capture alone may show connection metadata but not the readable request body or headers unless you also control decryption.

So if your question is really "what left my process in readable form," a proxy or application-level inspection is usually better than raw packet capture.

If You Use httpx Instead of requests

The same idea applies to other clients. For httpx, event hooks make request inspection straightforward:

python
1import httpx
2
3
4def log_request(request: httpx.Request):
5    print(request.method, request.url)
6    print(request.headers)
7    print(request.content)
8
9
10with httpx.Client(event_hooks={"request": [log_request]}) as client:
11    client.post("https://httpbin.org/post", json={"name": "Ada"})

The important idea is consistent across libraries: inspect the request object before or during sending, or route traffic through a proxy.

Be Careful with Sensitive Data

Debugging full requests can expose:

  • bearer tokens
  • cookies
  • passwords
  • API keys
  • personal data in bodies or headers

If you log requests in shared environments, redact sensitive headers and payload fields first.

Example:

python
def redact_headers(headers):
    hidden = {"authorization", "cookie"}
    return {k: ("<redacted>" if k.lower() in hidden else v) for k, v in headers.items()}

This is especially important if logs are shipped to centralized logging systems.

Common Pitfalls

  • Printing the response and assuming it shows the original request details automatically.
  • Using packet capture alone for HTTPS traffic and expecting to see readable request contents.
  • Forgetting that session defaults, authentication, or redirects may modify the request before it is sent.
  • Logging raw requests in production without redacting tokens, cookies, or personal data.
  • Inspecting only the URL and ignoring headers and body, which is often where the real bug lives.

Summary

  • With requests, inspect a PreparedRequest to see the method, URL, headers, and body before sending.
  • After sending, use response.request to inspect what was actually used.
  • Enable urllib3 and http.client debug logging for deeper protocol output.
  • Use a proxy such as mitmproxy when you need to inspect actual outbound HTTPS traffic.
  • Always treat full-request logging as sensitive because it may expose credentials or personal data.

Course illustration
Course illustration

All Rights Reserved.