Python
Requests module
Proxies
Web scraping
Network programming

Proxies with Python 'Requests' module

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

Python requests supports HTTP and HTTPS proxies directly, which makes it a practical choice for debugging traffic, routing requests through corporate networks, or controlling outbound IP origin. The important parts are using the right proxy URL format, deciding whether configuration belongs per request or per session, and handling TLS and authentication intentionally.

Pass Proxy Settings Per Request

The basic API accepts a dictionary keyed by protocol.

python
1import requests
2
3proxies = {
4    "http": "http://127.0.0.1:8080",
5    "https": "http://127.0.0.1:8080",
6}
7
8response = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
9print(response.status_code)
10print(response.text)

Always add a timeout. Proxy hops introduce more ways for a request to stall.

Use a Session for Repeated Requests

When multiple requests share the same proxy configuration, a Session keeps the code cleaner and reuses underlying connections.

python
1import requests
2
3session = requests.Session()
4session.proxies.update(
5    {
6        "http": "http://127.0.0.1:8080",
7        "https": "http://127.0.0.1:8080",
8    }
9)
10
11for path in ["/ip", "/headers"]:
12    response = session.get(f"https://httpbin.org{path}", timeout=10)
13    print(path, response.status_code)

This is usually the better structure for scraping scripts, test utilities, or service clients.

Handle Proxy Authentication Safely

Authenticated proxies typically embed credentials in the proxy URL. If the username or password contains reserved characters, URL-encode them first.

python
1import requests
2from urllib.parse import quote
3
4username = quote("demo-user")
5password = quote("p@ss:word")
6proxy_url = f"http://{username}:{password}@proxy.example.com:3128"
7
8response = requests.get(
9    "https://httpbin.org/ip",
10    proxies={"http": proxy_url, "https": proxy_url},
11    timeout=10,
12)
13print(response.status_code)

Without encoding, characters such as @ or : can break the URL parser and produce misleading authentication errors.

Use Environment Variables When the Whole Process Shares the Proxy

For command-line programs or multi-module tools, environment variables can be simpler than passing a proxy dictionary around.

bash
1export HTTP_PROXY=http://127.0.0.1:8080
2export HTTPS_PROXY=http://127.0.0.1:8080
3export NO_PROXY=localhost,127.0.0.1,.internal.example.com
4python app.py

That pattern is common in CI environments and inside corporate networks.

Deal with TLS Verification Correctly

HTTPS through a proxy can fail because the proxy presents a certificate chain the client does not trust. The right fix is usually to provide the appropriate CA bundle, not to disable verification permanently.

python
1import requests
2
3response = requests.get(
4    "https://httpbin.org/get",
5    proxies={"https": "http://127.0.0.1:8080"},
6    verify="/path/to/corp-ca.pem",
7    timeout=10,
8)
9print(response.status_code)

If you disable TLS verification during debugging, keep that change local and temporary.

Add Retries When Proxies Are Unstable

Some proxy networks fail intermittently. A small retry policy is reasonable for idempotent requests.

python
1import requests
2from requests.adapters import HTTPAdapter
3from urllib3.util.retry import Retry
4
5session = requests.Session()
6session.proxies.update({
7    "http": "http://127.0.0.1:8080",
8    "https": "http://127.0.0.1:8080",
9})
10
11retry = Retry(total=3, backoff_factor=0.5, status_forcelist=[429, 500, 502, 503, 504])
12adapter = HTTPAdapter(max_retries=retry)
13session.mount("http://", adapter)
14session.mount("https://", adapter)
15
16print(session.get("https://httpbin.org/status/200", timeout=10).status_code)

That improves resilience without turning transient errors into endless hangs.

Verify That Traffic Is Really Going Through the Proxy

When debugging, hit an endpoint that reports the observed client IP or request headers. Compare the result with and without proxy configuration so you know the routing change is real.

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

Common Pitfalls

  • Configuring only the http proxy entry and assuming HTTPS traffic will use it too.
  • Embedding raw credentials with special characters and getting a malformed proxy URL.
  • Omitting timeouts, which makes proxy failures look like a frozen program.
  • Disabling TLS verification instead of installing the correct certificate authority bundle.
  • Forgetting NO_PROXY for internal hosts that should bypass the proxy entirely.

Summary

  • Pass proxies as a protocol-to-URL mapping in requests.
  • Use a Session when many requests share the same proxy settings.
  • URL-encode proxy credentials before inserting them into the proxy URL.
  • Prefer correct TLS trust configuration over disabling verification.
  • Add timeouts and limited retries so proxy-related failures remain diagnosable.

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.