python
http
requests
raw-request
web-development

Python requests - print entire http request raw?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

When HTTP calls fail, seeing the full outgoing request is often faster than guessing from partial logs. In Python requests, the most reliable inspection point is a PreparedRequest. That object contains final method, URL, headers, and body after request construction logic runs.

Prepare and Inspect the Final Request

Use requests.Request plus Session.prepare_request to inspect exactly what will be sent.

python
1import requests
2
3session = requests.Session()
4req = requests.Request(
5    method="POST",
6    url="https://httpbin.org/post",
7    headers={"X-Trace-Id": "abc-123"},
8    json={"name": "Ana", "active": True},
9)
10
11prepared = session.prepare_request(req)
12
13print("METHOD:", prepared.method)
14print("URL:", prepared.url)
15print("HEADERS:")
16for k, v in prepared.headers.items():
17    print(f"  {k}: {v}")
18print("BODY:", prepared.body)
19
20response = session.send(prepared, timeout=10)
21print("STATUS:", response.status_code)

This avoids confusion from unprepared request objects that may miss auto-added headers.

Build a Raw-Style HTTP Preview

For debugging, you can format the prepared request into raw-looking text.

python
1from urllib.parse import urlsplit
2
3
4def format_prepared(prepared):
5    parts = urlsplit(prepared.url)
6    lines = [f"{prepared.method} {parts.path or '/'}{('?' + parts.query) if parts.query else ''} HTTP/1.1"]
7    lines.append(f"Host: {parts.netloc}")
8
9    for k, v in prepared.headers.items():
10        lines.append(f"{k}: {v}")
11
12    lines.append("")
13
14    body = prepared.body
15    if isinstance(body, bytes):
16        body = body.decode("utf-8", errors="replace")
17
18    lines.append(body or "")
19    return "\n".join(lines)
20
21print(format_prepared(prepared))

This format is useful in ticket attachments and integration tests.

Dump Full Request and Response Together

requests-toolbelt can dump request and response bytes in one step.

python
1# pip install requests-toolbelt
2import requests
3from requests_toolbelt.utils import dump
4
5resp = requests.get("https://httpbin.org/get", timeout=10)
6data = dump.dump_all(resp)
7print(data.decode("utf-8", errors="replace"))

Use this in controlled debugging environments because output can be large and sensitive.

Enable Transport-Level Debug Logging

For deeper diagnostics in connection layers, turn on urllib3 and HTTP connection debug logging.

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

Disable this after troubleshooting to avoid log noise and sensitive data exposure.

Redact Secrets Before Logging

Never print tokens, cookies, or credentials directly. Redact sensitive headers before output.

python
1SENSITIVE = {"authorization", "cookie", "x-api-key"}
2
3
4def safe_headers(headers):
5    out = {}
6    for k, v in headers.items():
7        out[k] = "<redacted>" if k.lower() in SENSITIVE else v
8    return out
9
10print(safe_headers(prepared.headers))

Apply redaction consistently in all request-dump utilities.

Reusable Debug Wrapper

Keep this utility under source control. In shared SDK code, include an option flag to enable or disable request dumping per call. This prevents globally noisy logs and lets support engineers activate deep diagnostics only for failing endpoints. Structured request dump metadata such as correlation ID and timestamp also makes incident timelines much easier to reconstruct. A wrapper function keeps inspection behavior consistent.

python
1def send_with_debug(session, request):
2    prepped = session.prepare_request(request)
3    print(format_prepared(prepped))
4    return session.send(prepped, timeout=10)

This avoids repeated ad hoc logging code and reduces mistakes.

Testing Outgoing Requests

When requests are signed or modified by middleware, capture request snapshots before and after each processing step. This staged comparison makes it easier to find exactly where headers, query parameters, or body content changed unexpectedly. In unit tests, you can assert request composition by mocking Session.send and checking prepared fields. This catches errors in headers and payload structure before network calls happen.

For integration tests, compare formatted request snapshots while keeping redacted fields deterministic.

Common Pitfalls

  • Logging Request instead of PreparedRequest and missing final headers.
  • Printing binary bodies directly without safe decoding.
  • Leaving transport debug logging enabled in production.
  • Logging credentials and tokens in plain text.
  • Assuming body serialization is unchanged after auth middleware layers.

Summary

  • Use PreparedRequest to inspect the true outgoing HTTP request.
  • Build raw-style previews for readable diagnostics.
  • Use toolbelt dumps when full request-response context is needed.
  • Redact sensitive headers and payload fields before logging.
  • Centralize debug utilities for consistent and safe observability.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.