# 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.

Source: https://howhttpworks.com/methods/connect
Last reviewed: 2026-10-04

> **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:

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

```http
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](https://www.rfc-editor.org/rfc/rfc9110#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](https://howhttpworks.com/status-codes/407) with a `Proxy-Authenticate` challenge, and the client retries with [Proxy-Authorization](https://howhttpworks.com/headers/proxy-authorization):

```http
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:

```bash
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:

```text
* 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](https://www.rfc-editor.org/rfc/rfc9113#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](https://www.rfc-editor.org/rfc/rfc8441) 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):

```text
: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](https://www.rfc-editor.org/rfc/rfc9220) 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](https://howhttpworks.com/headers/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](https://www.rfc-editor.org/rfc/rfc9298) 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.

## Related

[OPTIONS](https://howhttpworks.com/methods/options) is the other method that does not fit the read/write mould. For the proxy-side status code see [407 Proxy Authentication Required](https://howhttpworks.com/status-codes/407).
