HTTP/2, proxies and TLS

HTTPX · Advanced · 11 min

These connection-level options matter for performance against modern APIs and for corporate networks.

HTTP/2 needs an extra dependency: pip install "httpx[http2]" (it pulls in h2). Then create a client with http2=True. HTTPX negotiates the protocol during the TLS handshake (ALPN) and falls back to HTTP/1.1 if the server does not support it; check r.http_version. The benefit is multiplexing: many concurrent requests share one connection, which mostly matters with AsyncClient and many parallel requests to one host. For sequential requests the gain is small.

Proxies: since 0.28 use proxy="http://user:pass@proxy:8080" for all traffic. For per-scheme or per-host routing, pass mounts= mapping URL patterns to transports:

mounts={"https://": httpx.HTTPTransport(proxy="http://corp:3128"),
        "https://internal.example.com": httpx.HTTPTransport()}

The most specific pattern wins. SOCKS proxies need httpx[socks]. Environment variables HTTP_PROXY, HTTPS_PROXY, ALL_PROXY and NO_PROXY are honoured while trust_env=True.

TLS: verification is on and uses certifi by default. The recommended way to customise it is an ssl.SSLContext, e.g. ssl.create_default_context(cafile="corp-ca.pem") or one built with truststore to use the operating system's store, passed as verify=ctx. Client certificates are loaded into the context with ctx.load_cert_chain(...). Older verify="path" and cert= arguments are deprecated. SSL_CERT_FILE and SSL_CERT_DIR are respected with trust_env.

Example

import ssl
import httpx

# HTTP/2 (requires: pip install "httpx[http2]")
# with httpx.Client(http2=True) as c:
#     print(c.get("https://www.python-httpx.org").http_version)  # HTTP/2

ctx = ssl.create_default_context()        # e.g. cafile="corp-ca.pem"
# ctx.load_cert_chain("client.crt", "client.key")   # mutual TLS

client = httpx.Client(
    verify=ctx,
    mounts={
        "https://": httpx.HTTPTransport(verify=ctx, proxy="http://proxy.corp:3128"),
        "https://internal.example.com": httpx.HTTPTransport(verify=ctx),  # no proxy
    },
)
pick = client._transport_for_url          # private helper, for illustration only
print(pick(httpx.URL("https://internal.example.com/x")) is pick(httpx.URL("https://example.org")))
# False: the internal host gets its own (unproxied) transport
print(ctx.verify_mode == ssl.CERT_REQUIRED)   # True
client.close()

Remember

  • http2=True plus the http2 extra; check r.http_version
  • proxy= for one proxy, mounts= for per-scheme or per-host routing
  • Pass an ssl.SSLContext as verify= to customise CAs and client certs

Watch out for

  • Using the removed proxies= argument from older HTTPX versions.

Check yourself

  1. When does HTTP/2 give the biggest benefit with HTTPX?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.python-httpx.org/http2/