Tor’s support for HTTP CONNECT, and extensions

As of May 2025, this document is non-normative, since some of the documented C behavior design is tremendously ad-hoc, and is likely to change in the future.

For behaviors that we want to add, see torspec#337.

Once we are satisfied with it, we’ll remove this disclaimer.

The C Tor implementation supports the HTTP CONNECT protocol for providing a TCP tunnel over the Tor network.

HTTP is more readily extensible than SOCKS, and so it allows applications and Tor clients to better communicate structured information about their needs.

Here we will outline the amount of HTTP support that Tor provides, and describe the extensions that it supports.

Arti doesn’t support HTTP CONNECT yet as of this writing (May 2025), but likely will in the future. Specifying Tor’s behavior with this document is the first step in making Arti’s HTTP CONNECT implementation compatible.

HTTP CONNECT support

A Tor client supporting HTTP CONNECT should implement the CONNECT method as specified in RFC 9110. It SHOULD NOT support any other HTTP methods.

HTTP 1.0 support is mandatory; other HTTP versions are optional.

As with other proxy ports, clients SHOULD NOT listen on publicly reachable addresses.

All requests received at the HTTP CONNECT port should be routed over the Tor network, or rejected: none should be forwarded in any other way.

Error codes

Tor provides the following mapping from Tor END reasons to HTTP error codes:

These aren’t necessarily sensible, and will likely change in the future.

END reasonHTTP status code
MISC500
RESOLVEFAILED404
NOROUTE404
CONNECTREFUSED403
EXITPOLICY503
DESTROY502
DONE1502
TIMEOUT504
HIBERNATING502
INTERNAL502
RESOURCELIMIT502
CONNRESET503
TORPROTOCOL502
ENTRYPOLICY403
NOTADIRECTORY2500
anything else500

Implementations SHOULD include information about the actual error code in the Reason-Phrase part of the Status-Line.

Note that once a connection has succeeded, no subsequent status code for an END reason will be sent, since Tor already sent a “200 OK”.

TODO: Specify specific return values, and/or messages, and/or headers, for onion-service-specific issues.

Standard headers

Except as noted here, and in “Extended headers” below, the C tor implementation doesn’t parse or send any HTTP headers.

If this is non-conformant, we should change it.

Proxy-Authorization

The Proxy-Authorization header is sent by the client, and used as an input for stream isolation; see “Stream isolation” below.

C Tor does not impose an upper length limit on this header.

Extensions

Here we describe Tor’s behaviors that are in addition to those of a standard HTTP CONNECT tunnel.

Determining IP version preference

Tor applies the same rules to HTTP CONNECT requests as it does to SOCKS5 requests when determining which IP versions to request or allow.

Extended headers

All headers specified for use with Tor will begin with X-Tor-.

X-Tor-Stream-Isolation

The X-Tor-Stream-Isolation header is sent by the client, and used as an input for stream isolation; see “Stream isolation” below.

It can appear only once; subsequent occurrences are ignored.

C Tor does not impose an upper length limit on this header.

Stream isolation

The isolation value of an HTTP CONNECT stream is a tuple of its “Proxy-Authorization” header (if any) and its “X-Tor-Stream-Isolation” header (if any).

When possible, applications should prefer X-Tor-Stream-Isolation: Proxy-Authorization is supported in this role only to work with HTTP libraries that make it difficult to set arbitrary headers.

In contrast to the HTTP spec, implementations MAY ignore HTTP space-folding rules when comparing these headers for equality.

Two HTTP CONNECT streams are not allowed to share a circuit if they have different isolation values.

For stream isolation purposes, C Tor considers a “Proxy-Authentication: X”, “X-Tor-Stream-Isolation: Y” tuple to be equivalent to SOCKS5 authentication with username=X and password=Y.

We are unlikely to preserve this equivalence in Arti without further examination.

For more information on stream isolation, including other factors that can prevent streams from sharing circuits, see proposal 171.

TODO: Integrate proposal 171 this into the spec. (See torspec#269.)


  1. A regular DONE reason, if it arrives after a CONNECTED message, won’t trigger a status line, since the CONNECTED message already triggered a “200 OK” status line.

  2. Clients should be unable to trigger a NOTADIRECTORY reason, since it should only happen in response to a BEGINDIR message.