httpx HTTP/2: enable http2=True (tested)
pip install "httpx[http2]", then httpx.Client(http2=True). HTTP/2 is negotiated over HTTPS only (unless http1=False); confirm with response.http_version.
httpx speaks HTTP/1.1 by default. HTTP/2 needs the optional h2 package and http2=True on a Client or AsyncClient, and it is only used when the server agrees. Measured with httpx 0.28.1, h2 4.4.1 and hypercorn on Python 3.13.5. The test server is this ASGI app, run by hypercorn with a self-signed certificate on an HTTPS port and a plain HTTP port (srv.start() returns both URLs):
async def app(scope, receive, send):
if scope["type"] != "http":
return
body = f'{scope["http_version"]} client_port={scope["client"][1]}'.encode()
await send({"type": "http.response.start", "status": 200,
"headers": [(b"content-type", b"text/plain"), (b"content-length", str(len(body)).encode())]})
await send({"type": "http.response.body", "body": body})
1. ImportError: the h2 package is not installed
import httpx
try:
httpx.Client(http2=True)
except ImportError as e:
print("ImportError:", e)
Output:
ImportError: Using http2=True, but the 'h2' package is not installed. Make sure to install httpx using `pip install httpx[http2]`.
Fix: pip install "httpx[http2]" (or pip install h2). The error is raised when the client is created, not on the first request.
2. Enable HTTP/2 and check it with response.http_version
import httpx, srv
https_url, http_url = srv.start()
for http2 in (False, True):
with httpx.Client(http2=http2, verify="cert.pem") as c:
r = c.get(https_url)
print(f"http2={http2}: r.http_version={r.http_version!r} server saw {r.text}")
Output:
http2=False: r.http_version='HTTP/1.1' server saw 1.1 client_port=46280
http2=True: r.http_version='HTTP/2' server saw 2 client_port=46292
Over HTTPS, httpx offers h2 via TLS ALPN and the server picks. Check r.http_version to see what was actually used; http2=True is a request, not a guarantee.
3. Plain http:// stays HTTP/1.1 unless you disable HTTP/1
import httpx, srv
https_url, http_url = srv.start()
with httpx.Client(http2=True) as c:
r = c.get(http_url)
print("http:// with http2=True:", r.http_version)
with httpx.Client(http1=False, http2=True) as c:
r = c.get(http_url)
print("http:// with http1=False, http2=True:", r.http_version)
Output:
http:// with http2=True: HTTP/1.1
http:// with http1=False, http2=True: HTTP/2
httpx does not do the Upgrade: h2c dance. On http:// URLs it uses HTTP/1.1 even with http2=True. With http1=False it sends HTTP/2 directly (prior knowledge), which only works if the server accepts cleartext HTTP/2.
4. httpx.get() has no http2 argument
import httpx
try:
httpx.get("https://example.com", http2=True)
except TypeError as e:
print("TypeError:", e)
Output:
TypeError: get() got an unexpected keyword argument 'http2'
HTTP/2 is a client setting, so the top-level httpx.get() helpers do not accept it. Create a Client(http2=True).
5. Why use it: one connection for concurrent requests
import asyncio, httpx, srv
https_url, http_url = srv.start()
async def run(http2):
async with httpx.AsyncClient(http2=http2, verify="cert.pem") as c:
rs = await asyncio.gather(*(c.get(https_url) for _ in range(10)))
ports = {r.text.split("client_port=")[1] for r in rs}
print(f"http2={http2}: 10 concurrent requests, {rs[0].http_version}, {len(ports)} connection(s)")
asyncio.run(run(False)); asyncio.run(run(True))
Output:
http2=False: 10 concurrent requests, HTTP/1.1, 10 connection(s)
http2=True: 10 concurrent requests, HTTP/2, 1 connection(s)
With HTTP/1.1, ten concurrent requests opened ten TCP+TLS connections. With HTTP/2 they were multiplexed over one. For sequential requests on a reused client the difference is small; HTTP/2 helps mostly with many concurrent requests to the same host.
Related: httpx AsyncClient connection pool limits, httpx SSL certificate verify failed, ERR_HTTP2_PROTOCOL_ERROR, httpx vs requests differences.
Frequently asked questions
Does httpx support HTTP/2?
Yes, with pip install "httpx[http2]" and Client(http2=True) or AsyncClient(http2=True). It is off by default.
Why is response.http_version still HTTP/1.1?
The server did not negotiate h2, or the URL is plain http://. httpx only uses HTTP/2 over http:// with http1=False.
Can I pass http2=True to httpx.get()?
No. In httpx 0.28.1 it raises TypeError: get() got an unexpected keyword argument 'http2'. Use a Client.
Written with AI assistance. Measured on 2026-10-23 with httpx 0.28.1, h2 4.4.1 and hypercorn on Python 3.13.5 against a local server. Each block was run verbatim and the output pasted unedited. PlainHeaders is our extension; httpx is free.