windmill-labs/windmill · error
HTTP proxy sent unexpected bytes after CONNECT response
Error message
HTTP proxy sent unexpected bytes after CONNECT response
What it means
After reading the CONNECT response, `http_connect_tunnel` checks `BufReader`'s internal buffer. A conforming HTTP proxy sends nothing after the CONNECT response until the client speaks. If buffered bytes remain, the proxy already sent extra data (pipelined response, injected banner, HTTP redirect, or a non-CONNECT response body); handing the raw socket to the TLS layer would silently discard those bytes and corrupt the WebSocket/TLS handshake, so the function fails fast.
Source
Thrown at backend/windmill-trigger-websocket/src/proxy.rs:308
}
if !status_ok {
return Err(io::Error::new(
io::ErrorKind::Other,
format!(
"HTTP proxy CONNECT to {} rejected: {}",
host_header,
status_line.trim_end()
),
));
}
// A conforming proxy stays silent after the CONNECT response until the
// client speaks. If our read buffer is non-empty, the proxy spoke
// first — handing the raw socket to TLS would silently drop those
// bytes and break the handshake.
if !reader.buffer().is_empty() {
return Err(io::Error::new(
io::ErrorKind::Other,
"HTTP proxy sent unexpected bytes after CONNECT response",
));
}
Ok(reader.into_inner())
}
#[cfg(test)]
mod tests {
//! The single live test (`http_connect_tunnel_…_unwraps_stream`) drives
//! a real `TcpListener` masquerading as a proxy and verifies both the
//! on-the-wire CONNECT request and that the returned `TcpStream`
//! actually carries tunneled bytes. The other proxy-URL and NO_PROXY
//! checks are kept under `#[ignore]` for manual debugging — they cover
//! logic that's mostly delegated to `url::Url::parse` and trivial
//! string matching, so re-running them on every CI build is low ROI.
use super::*;View on GitHub (pinned to e474e8803c)
Solutions
- Verify the proxy URL scheme matches the proxy listener (plain http:// CONNECT port vs https:// TLS port) and fix the scheme
- Confirm the endpoint is an HTTP CONNECT proxy, not a SOCKS proxy (use a SOCKS-aware config or change the proxy)
- Capture traffic (tcpdump/Wireshark on the client↔proxy link) to see what bytes the proxy sends after the response
- Disable or reconfigure any TLS-interception appliance (e.g. MITM AV/firewall module) for this destination
Example fix
// before let proxy = "http://proxy.corp:443"; // 443 is the TLS listener -> banner bytes after CONNECT // after let proxy = "https://proxy.corp:443"; // or "http://proxy.corp:3128" for the plain listener
Defensive patterns
Strategy: validation
Validate before calling
// fail fast if the endpoint is not a plain HTTP CONNECT proxy
let parsed = url::Url::parse(&proxy_url)?;
let is_plain_http = parsed.scheme() == "http";
let is_socks = matches!(parsed.scheme(), "socks4" | "socks4a" | "socks5");
if !is_plain_http && !is_socks {
return Err(anyhow!("unsupported/ambiguous proxy scheme: {}", parsed.scheme()));
} Try / catch
match connect_async_with_proxy(&url, &proxy).await {
Err(WsError::Io(e)) if e.to_string().contains("unexpected bytes after CONNECT") => {
eprintln!("{} is not a conforming CONNECT proxy: {}", proxy, e);
// fix scheme/port or replace the intercepting proxy before retrying
}
Err(e) => return Err(e.into()),
Ok(_) => {}
} Prevention
- Match the proxy URL scheme to the listener: never point an http:// CONNECT client at a TLS port (or vice versa)
- Confirm the endpoint is HTTP CONNECT, not SOCKS — SOCKS proxies need a SOCKS client, not CONNECT tunneling
- Exclude this destination from TLS-interception/MITM appliances in the network path
- Add a preflight CONNECT check in CI/deployment to detect non-conforming proxies early
When it happens
Trigger: Calling `connect_async_with_proxy` (via `test_connection` or `get_consumer`) when the proxy sends bytes immediately after its CONNECT response — e.g. it sent a redirect body, an error page after a non-standard status, TLS on the proxy link, or is not actually an HTTP CONNECT proxy (some intercepting/mTLS proxies greet the client first).
Common situations: Pointing the client at a TLS/HTTPS proxy port with a plaintext CONNECT; pointing at a SOCKS proxy or transparent interception proxy that isn't CONNECT-capable; an anti-virus / SSL-inspection appliance injecting a banner; a misbehaving proxy that pipelines responses.
Related errors
- HTTP proxy closed connection before sending CONNECT response
- HTTP proxy CONNECT to {} rejected: {}
- Failed to fetch foundation models for AWS Bedrock
- Failed to fetch models for provider ${provider}
- Bad Gateway
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/b4878a52f30bd00e.
Report an issue: GitHub.