Published: May 16, 2016
Last updated: January 30, 2017

Domain Fronting Explained

Domain Fronting is the single best way to circumvent censorship by a firewall that ever happened. Among other use cases, Tor Browser is now shipping with it’s new Pluggable Transport meek, which makes the Tor traffic look like a browser talking to a completely harmless website like Google. Because I find domain fronting so cool and at the same time it’s so simple, I want to quickly explain it. I’ll demonstrate the whole thing using a Linux shell.

How HTTP(S) works

First, we have to cover the basics of the HTTP(S) protocol. If you connect to www.parckwart.de for example, you establish a TLS connection to whatever IP address is resolved by asking the DNS server for www.parckwart.de. Once the TLS connection is up, the browser sends the HTTP header, (at least) consisting of what page you want to get, the HTTP version your browser speaks and the hostname of the server from which it wants to fetch the webpage. You can see here for example, what headers your browser sends to a typical webpage.

Here’s how you can do it manually in your shell using openssl’s s_client:

$ openssl s_client -host www.parckwart.de -port 443
CONNECTED(00000003)
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X1
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
 0 s:/CN=www.parckwart.de
   i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X1
 1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X1
   i:/O=Digital Signature Trust Co./CN=DST Root CA X3
---
<snip>
---
GET / HTTP/1.1
Host: www.parckwart.de

HTTP/1.1 200 OK
Date: Sun, 15 May 2016 17:34:18 GMT
Server: Apache/2.4.10
Vary: Accept-Encoding
Transfer-Encoding: chunked
Content-Type: text/html

119
        <!doctype html>

        <html>
                <head>
                        <meta charset="utf-8">
                        <meta name="author" content="Parckwart">
<snip>

I cut out a lot of lines, because s_client outputs all kinds of info about the TLS connection we don’t care about in this article. All I actually typed here, was the openssl shell command and the headers I sent:

GET / HTTP/1.1
Host: www.parckwart.de

That’s all it takes to get the homepage of www.parckwart.de.

What happens if we send the server a host header that it isn’t responsible for?

$ openssl s_client -host www.parckwart.de -port 443
---
GET / HTTP/1.1
Host: www.youtube.com

HTTP/1.1 403 Forbidden
Date: Sun, 15 May 2016 19:12:00 GMT
Server: Apache/2.4.10
Content-Length: 209
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access /
on this server.<br />
</p>
</body></html>
</pre>

It more or less tells you to go away. The exact response may vary depending on the web server used and its configuration. But you shouldn’t get the page requested.

How Domain Fronting works

Huge internet companies like Google, Amazon and Microsoft offer their web services using CDNs (Content Delivery Networks). Not only do they offer their own services using these, but also the ones you can host on their servers (Amazon CloudFront for instance). That means that thousands of internet domains are all pointing to the same CDN. At CloudFront the domains include d3dsacqprgcsqh.cloudfront.net (9GAG), deayhd4nq31b0.cloudfront.net (DealPly), a0.awsstatic.com (General domain for static web content of AWS) and so on.

So, how is this useful? Let’s look at how Tor takes advantage of this: The Tor Project hosts a simple nice server that you can use as a Tor entry point. In Amazon’s CDN, that’s d2zfqthxsdq309.cloudfront.net. If you would just connect to this in the usual way, it could be easily censored by blocking that domain in the DNS or in many other ways. But what happens if we connect to another domain of Amazon’s CDN but set d2zfqthxsdq309.cloudfront.net as our host header?

$ openssl s_client -host deayhd4nq31b0.cloudfront.net -port 443
GET / HTTP/1.1
Host: d2zfqthxsdq309.cloudfront.net

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 38
Connection: keep-alive
Date: Fri, 09 Sep 2016 07:54:05 GMT
X-Cache: Miss from cloudfront
Via: 1.1 f32dfb4a33594b7c1c1bbebfe50a0bfd.cloudfront.net (CloudFront)
X-Amz-Cf-Id: wSCO0ZWsMHUT5YjK3rmJvCS1vkhwWlAgGFUZqDy0Xag0V0wKZObWZg==

I’m just a happy little web server.

We actually can talk to that Tor entry while looking like a web browser talking to DealPly, 9GAG, Amazon or a whole lot of other websites from the outside. The host header is only transmitted via TLS, so no firewall can see it. Also, we never asked our DNS server for d2zfqthxsdq309.cloudfront.net. A censor like the Great Firewall of China cannot see, if you are actually talking to 9GAG. It also hopefully won’t dare to block this kind of sites in general because the colateral damage would be to big. Just to block this, the censor would have to block any single AWS site, in meek’s case also every Microsoft Azure service as well, making the Internet for a lot of people pretty damn unusable.

Update (September 9, 2016): Originally there was also a meek bridge in Google’s CDN AppEngine. Google however didn’t seem to like the idea and suspended the bridge. I replaced all the Google examples in this article with Amazon.

Update (December 23, 2016): Tor Browser now ships with meek included, updated that.

Update (January 30, 2017): Improved a potentially misleading sentence (Thank you, Peter).