Alt-Svc header does not include Tor onion address despite enabling Onion Routing

I would like my web site at to be served as an onion service via the Cloudflare Onion Routing feature in the domain’s Network tab. However, it appears that Cloudflare is not sending the appropriate Alt-Svc HTTP header needed for a browser like Tor Browser or Brave to detect that an onion version of the site is available. The only Alt-Svc entries I see are for HTTP/3 with QUIC, and if I disable that feature in the Network tab the header is completely missing.

I’ve searched the community forum and found a few others describing the same problem, but no offered solutions.

Here is a curl command displaying the headers with verbose output:

% curl –verbose –location –head
*   Trying 2606:4700:3033::6815:1732:443…
* Connected to (2606:4700:3033::6815:1732) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
* (304) (OUT), TLS handshake, Client hello (1):
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=US; ST=California; L=San Francisco; O=Cloudflare, Inc.;
*  start date: Jun  7 00:00:00 2022 GMT
*  expire date: Jun  7 23:59:59 2023 GMT
*  subjectAltName: host "" matched cert's ""
*  issuer: C=US; O=Cloudflare, Inc.; CN=Cloudflare Inc ECC CA-3
*  SSL certificate verify ok.
* Using HTTP2, server supports multiplexing
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x123811400)
> Host:
> user-agent: curl/7.79.1
> accept: */*
* Connection state changed (MAX_CONCURRENT_STREAMS == 256)!
< HTTP/2 200
HTTP/2 200
< date: Fri, 14 Oct 2022 17:08:03 GMT
date: Fri, 14 Oct 2022 17:08:03 GMT
< content-type: text/html; charset=UTF-8
content-type: text/html; charset=UTF-8
< link: <>; rel=””, <>; rel=shortlink
link: <>; rel=””, <>; rel=shortlink
< strict-transport-security: max-age=31536000
strict-transport-security: max-age=31536000
< content-security-policy: upgrade-insecure-requests
content-security-policy: upgrade-insecure-requests
< x-content-type-options: nosniff
x-content-type-options: nosniff
< x-xss-protection: 1; mode=block
x-xss-protection: 1; mode=block
< expect-ct: max-age=7776000, enforce
expect-ct: max-age=7776000, enforce
< referrer-policy: no-referrer-when-downgrade
referrer-policy: no-referrer-when-downgrade
< cache-control: max-age=300
cache-control: max-age=300
< expires: Fri, 14 Oct 2022 17:13:01 GMT
expires: Fri, 14 Oct 2022 17:13:01 GMT
< x-endurance-cache-level: 2
x-endurance-cache-level: 2
< x-nginx-cache: WordPress
x-nginx-cache: WordPress
< cf-cache-status: DYNAMIC
cf-cache-status: DYNAMIC
< report-to: {”endpoints”:[{"url":"https:\/\/\/report\/v3?s=ZscP7XXFtQlj1GtngNNev4pJNXvF3%2FMtcYv0fh0vnkCouphAbPsRXGjU0djka8bemW2vW6%2FrAk0tkkyeYd66rvc3awKhbUh%2BB8z%2Fx8NOunr%2FAt6xaURJ4uL7SJ77L%2BPPJ1IOTvshhsseMPqwJvs%3D"}],”group”:”cf-nel”,”max_age”:604800}
report-to: {”endpoints”:[{"url":"https:\/\/\/report\/v3?s=ZscP7XXFtQlj1GtngNNev4pJNXvF3%2FMtcYv0fh0vnkCouphAbPsRXGjU0djka8bemW2vW6%2FrAk0tkkyeYd66rvc3awKhbUh%2BB8z%2Fx8NOunr%2FAt6xaURJ4uL7SJ77L%2BPPJ1IOTvshhsseMPqwJvs%3D"}],”group”:”cf-nel”,”max_age”:604800}
< nel: {”success_fraction”:0,”report_to”:”cf-nel”,”max_age”:604800}
nel: {”success_fraction”:0,”report_to”:”cf-nel”,”max_age”:604800}
< server: cloudflare
server: cloudflare
< cf-ray: 75a1ea29ae91ad2d-ATL
cf-ray: 75a1ea29ae91ad2d-ATL
< alt-svc: h3=”:443”; ma=86400, h3-29=”:443”; ma=86400
alt-svc: h3=”:443”; ma=86400, h3-29=”:443”; ma=86400

* Connection #0 to host left intact

And here is a screenshot of my Network settings in the Cloudflare dashboard:

I just tried this, and it looks like that header is only set when you visit the site from the onion network and not if you visit normally. Looking over the blog post, this appears to be expected. Cloudflare only adds that header if you connect to a Cloudflare service via the onion network.

Unfortunately, Cloudflare’s list of exits is primarily IPv4, it only has a small fraction of IPv6 exits listed, so the fact that they only add the alt-svc header including the .onion when they recognize a tor exit is problematic.

You can use’s test to experiment with this, but while IPv4 usually gets yellow (recognized as tor), IPv6 is nearly always red (not recognized as coming from tor). Even when I get to yellow, today I’m unable to get to green even after a refresh, and I’m not quite sure why as green wasn’t a problem in the past.

Also it seems like Cloudflare only returns the tor part of the alt-svc header to things that look like tor browser so if you’re testing from curl you need to add -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0".

As much as I am a fan of tor, and love the idea, in my opinion this feature is too broken to be relied upon due to the fact that it only works when Cloudflare suspects the connection is coming from a tor exit.

On the other hand, it is also harmless to leave enabled since it fails over to using a regular exit node and hitting the nearest Cloudflare POP anyway. It might occasionally push a bit of traffic over to the .onion, but not even close to all.

1 Like

This topic was automatically closed 15 days after the last reply. New replies are no longer allowed.