Skip to content

fix: never send HTTP/3 requests past the proxy, make HTTP/3 opt-in in the Rust crate - #595

Draft
metalwarrior665 wants to merge 1 commit into
masterfrom
feat/http3-opt-in
Draft

metalwarrior665 wants to merge 1 commit into
masterfrom
feat/http3-opt-in

Conversation

@metalwarrior665

@metalwarrior665 metalwarrior665 commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

HTTP/3 bypassed the proxy

reqwest sends HTTP/3 requests through its own QUIC connector (h3_client/connect.rs), which resolves the host and connects directly. Only the TCP connector knows about proxies; that's true of both the crates.io release and the patched revision impit uses. So with with_http3() and a proxy:

  1. The first request to a host goes through the proxy over HTTP/2.
  2. When the host advertises h3, through Alt-Svc or its HTTPS DNS record, impit caches the host as HTTP/3-capable.
  3. Later requests to the host go out over QUIC from the machine's own IP address.

Reproduced on master. I used with_http3() with a local logging CONNECT proxy and fetched https://www.google.com/:

  • The normal requests went through the proxy: one CONNECT www.google.com:443 each.
  • The request with http3_prior_knowledge never reached the proxy. strace shows the QUIC packets going straight from the process to 142.251.153.119:443.
  • The HTTPS-record lookup of H3Engine also went straight to 8.8.8.8 / 8.8.4.4.

HTTP/3 is opt-in and off by default, so the default path is fine. The problem is only with http3: true plus a proxy.

Now, when a proxy is set, HTTP/3 is disabled with a warning. A proxy counts as set when it's given with with_proxy, or in HTTPS_PROXY / ALL_PROXY, the variables reqwest reads for HTTPS. A request with http3_prior_knowledge then fails with Http3Disabled. I picked this over failing at build time so existing http3: true + proxy setups keep working, just over HTTP/2. It's easy to flip if you'd rather error.

Unused QUIC setup on every client

reqwest's http3 feature also makes every client using use_preconfigured_tls build an HTTP/3 connector, even when HTTP/3 is never used. That means a quinn endpoint per client: a UDP socket, a driver task, and a zeroed receive buffer of at least 2 MB.

In a CPU profile of a Rust Instagram scraper that builds one impit client per proxy session, this endpoint setup took ~6% of all CPU, for a protocol it never uses.

HTTP/3 support is now behind a new http3 feature of the impit crate:

  • With the feature off, clients skip the QUIC setup, and hickory-* and tokio aren't needed.
  • The crate builds without --cfg reqwest_unstable.
  • impit-node and impit-python enable the feature, so their http3 option behaves as before.
  • For Rust users, with_http3() now needs features = ["http3"]. I removed it from the README and doc examples. The ImpitBuilder doc example combined it with a proxy, which is exactly the leaking setup.

CI now also runs cargo test -p impit --features http3, and the docs are built with the feature on.

🤖 Generated with Claude Code

https://claude.ai/code/session_0148RYR4BTGyCYHENSRZjC3S

… the Rust crate

reqwest sends HTTP/3 requests through its own QUIC connector, which ignores
proxies. With `with_http3()` and a proxy, the first request to a host went
through the proxy, and once the host's `Alt-Svc` header advertised h3, later
requests went out over QUIC from the machine's own IP address. HTTP/3 is now
disabled (with a warning) when a proxy is set, either with `with_proxy` or in
`HTTPS_PROXY` / `ALL_PROXY`.

reqwest's `http3` feature also makes every client set up a QUIC endpoint (a
UDP socket, a driver task and a receive buffer of at least 2 MB), even when
HTTP/3 is never used. HTTP/3 support is now behind the `http3` feature of the
`impit` crate, so clients without it skip that setup, and the crate builds
without `--cfg reqwest_unstable`. The Node and Python bindings enable the
feature, so their `http3` option works as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0148RYR4BTGyCYHENSRZjC3S

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants