How HTTP Works

Method · unsafe · not idempotent

> CONNECT example.com:443 HTTP/1.1>  Host: example.com:443>  Proxy-Authorization: Basic dXNlcjpwYXNz

HTTP CONNECT Method: Proxy Tunnels Explained

HTTP CONNECT opens a TCP tunnel through a proxy so HTTPS can pass untouched. See the exact request and 200 response, 407 proxy auth, and extended CONNECT.

Reviewed 4 min readadvanced6 sourcesMarkdown
Safe
No
Idempotent
No
Cacheable
No
Request body
No defined semantics
Response body
No: a 2xx turns the connection into a tunnel
Spec
RFC 9110 §9.3.6
On this page

TL;DR: CONNECT host:port tells a proxy to open a TCP connection to that destination and then get out of the way. A 2xx reply means the tunnel is up and the next bytes are not HTTP anymore, which is how HTTPS works through a corporate or forward proxy.

What the exchange looks like

A browser behind an HTTP proxy that wants https://example.com/ does not send a GET to the proxy. It asks for a tunnel first:

CONNECT example.com:443 HTTP/1.1
Host: example.com:443
Proxy-Authorization: Basic dXNlcjpwYXNz
HTTP/1.1 200 Connection Established

Everything after that blank line belongs to the client and the origin. The client starts a TLS handshake, the proxy copies bytes without reading them, and the GET / is encrypted inside the tunnel. The proxy only ever learned example.com:443.

Rules worth knowing from RFC 9110 section 9.3.6:

  • The request target is authority-form, host:port, never a path or full URL. The port is required.
  • Any 2xx means “tunnel established”. The reason phrase is free text; Squid and others send Connection Established, which is where the common spelling comes from.
  • A server must not send Content-Length or Transfer-Encoding in a successful CONNECT response, and a client must ignore them if it sees them.
  • Anything other than 2xx is an ordinary HTTP response and the connection is not a tunnel.
  • CONNECT is neither safe nor idempotent in the RFC 9110 sense, and its responses are not cacheable.

Proxy authentication and refusals

If the proxy requires credentials it answers 407 with a Proxy-Authenticate challenge, and the client retries with Proxy-Authorization:

HTTP/1.1 407 Proxy Authentication Required
Proxy-Authenticate: Basic realm="corp-proxy"
Content-Length: 0

A bare 403 on CONNECT is a policy decision. Forward proxies commonly limit which destination ports can be tunnelled, because an open CONNECT to arbitrary ports turns the proxy into a relay to SMTP, SSH or internal services. Squid’s stock configuration, for instance, denies CONNECT to ports outside its SSL_ports list. If HTTPS to port 443 works but https://host:8443/ fails with a 403 from the proxy, that rule is the first thing to check.

Reproduce it

curl uses CONNECT automatically when you give it an HTTP proxy and an HTTPS URL:

curl -v -x http://proxy.example.net:3128 https://example.com/ -o /dev/null

The abridged verbose output shows the tunnel step before the TLS handshake:

* Establish HTTP proxy tunnel to example.com:443
> CONNECT example.com:443 HTTP/1.1
> Host: example.com:443
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 Connection established
<
* CONNECT phase completed

Add -U user:pass (--proxy-user) to send Proxy-Authorization. Lines marked > and < before the TLS handshake are proxy traffic; the origin’s own headers appear after it.

HTTP/2 and HTTP/3

Over HTTP/2, CONNECT is one stream rather than the whole connection, so one TCP connection to the proxy can carry many tunnels. RFC 9113 section 8.5 defines the request with :method = CONNECT and :authority = host:port; :scheme and :path are omitted. HTTP/3 uses the same shape (RFC 9114).

Extended CONNECT: WebSockets over HTTP/2 and HTTP/3

RFC 8441 adds a :protocol pseudo-header to CONNECT so a stream can bootstrap another protocol. The server must first advertise SETTINGS_ENABLE_CONNECT_PROTOCOL with value 1, and a client must not use :protocol until it has seen that setting. A WebSocket handshake then looks like this (HTTP/2 pseudo-headers shown):

:method = CONNECT
:protocol = websocket
:scheme = https
:path = /chat
:authority = server.example.com
sec-websocket-version = 13
origin = https://example.com

The server answers 200, not 101 Switching Protocols, and the stream then carries WebSocket frames. Note that in extended CONNECT :scheme and :path are present again. RFC 9220 is the same mechanism for HTTP/3. If a WebSocket works on HTTP/1.1 but breaks after you enable HTTP/2 at a proxy, check whether the proxy advertises extended CONNECT; clients that do not see the setting fall back to the classic Upgrade handshake on an HTTP/1.1 connection.

CONNECT-UDP and CONNECT-IP (MASQUE)

The IETF MASQUE working group builds on extended CONNECT to proxy things other than TCP. RFC 9298 defines :protocol = connect-udp, which proxies UDP datagrams (and therefore QUIC and HTTP/3) through an HTTP proxy. RFC 9484 does the same for IP packets with connect-ip. Both use the capsule protocol from RFC 9297. These are client-to-proxy protocols; an application server does not need to implement them.

Browsers and servers

  • Browser JavaScript cannot send CONNECT. The Fetch Standard treats CONNECT, TRACE and TRACK as forbidden methods.
  • A normal origin server or reverse proxy has no reason to accept CONNECT. Stock nginx is not a forward proxy and does not implement tunnelling for it; third-party modules such as ngx_http_proxy_connect_module exist for that purpose. Squid, Envoy and most cloud forward proxies do support it.
  • If CONNECT requests appear in your origin’s access log, they are usually scanners probing for an open proxy. A 405 or a dropped connection is the right answer.

OPTIONS is the other method that does not fit the read/write mould. For the proxy-side status code see 407 Proxy Authentication Required.

Frequently asked questions

What does the HTTP CONNECT method do?

It asks a proxy to open a TCP connection to the host and port in the request target and then relay bytes in both directions. After a 2xx response, HTTP framing stops and the connection is an opaque tunnel, which is how a browser carries TLS to an HTTPS site through an HTTP proxy.

Can the proxy see my HTTPS traffic when I use CONNECT?

It sees the target host and port in the CONNECT line (and, with proxy authentication, who you are), but the TLS handshake and everything after it happen between the client and the origin through the tunnel. A proxy only reads the content if it is configured to terminate TLS itself with its own certificate, which the client must be set up to trust.

Why does CONNECT return 403 or 407?

407 Proxy Authentication Required means the proxy wants credentials in Proxy-Authorization before it will tunnel. 403 usually means a proxy policy refused the destination; Squid, for example, ships a default rule that denies CONNECT to ports it does not list as SSL ports.

Can I send a CONNECT request with fetch or XMLHttpRequest?

No. The Fetch Standard lists CONNECT, TRACE and TRACK as forbidden methods, so browser JavaScript cannot issue them. Browsers send CONNECT themselves when a proxy is configured and the page is HTTPS.

What is extended CONNECT?

It is a variant defined in RFC 8441 for HTTP/2 and RFC 9220 for HTTP/3 that adds a :protocol pseudo-header, so a CONNECT stream can bootstrap another protocol. Its best-known use is WebSocket over a single HTTP/2 or HTTP/3 stream, and it is also the mechanism behind CONNECT-UDP (RFC 9298).

Sources

  1. MDN Web Docs: CONNECTdeveloper.mozilla.org
  2. RFC 9110 section 9.3.6: CONNECTrfc-editor.org
  3. RFC 9113 section 8.5: The CONNECT Method (HTTP/2)rfc-editor.org
  4. RFC 8441: Bootstrapping WebSockets with HTTP/2rfc-editor.org
  5. RFC 9220: Bootstrapping WebSockets with HTTP/3rfc-editor.org
  6. RFC 9298: Proxying UDP in HTTPrfc-editor.org

Keep going

Browse /search