TL;DR

Skip to Configuration section if you want to skip installation process and you are only interested in configuration details.

If your setup is simple and you don't need HAProxy load-balancer, skip to Nginx configuration.

Why should I care about HTTP/2?

There are already many articles out there about HTTP/2 and its benefits - and I encourage you to read them. I'll focus on points which are most important from my point of view.

Key benefits of HTTP/2:

  • It is binary (not textual like HTTP/1.1) and uses header compression. No more worries about header and cookies size.
  • Is fully multiplexed, can use one connection for parallelism. Your site performs much better in case it includes plenty of resources (fonts, CSS, JS, image files) because now they are all loaded in single TCP connection, in a non-blocking manner. Domain sharding and asset concatenation becomes an anti-pattern. In short: your website loads much faster.
  • It allows the server to push responses proactively into client caches (no support for that feature in Nginx yet).
  • It uses the new ALPN extension which allows for faster-encrypted connection. The encryption protocol is determined during initial connection.

Can I use it today?

Yes, you can and you should. As you can see on Can I Use service, all modern browsers now support HTTP/2, incl. IE11 and Edge. The only exceptions are in the mobile world with Opera Mini and Android Browser not supporting it. 

Moreover, the configuration described below ensures that clients that are not supporting HTTP/2, will fallback to HTTP/1.1. This is very important: your website should be accessible for older browsers or search engine bots.

Setup

I'll use CentOS 7 for my setup, but you can easily adjust all code snippets for other Linux distribution.

BTW: If your setup is simple and you don't need HAProxy, you can stick with just Nginx with presented http2 setup. In real world though you probably will use HAProxy as a load balancer and several Nginx back-end servers serving your app.

You will need:

  1. Site running over SSL. You can use dummy certificate if you do not have any (SIMPLE).
  2. Nginx 1.9.5 or newer (SIMPLE).
  3. HAProxy 1.6 or newer with OpenSSL 1.0.2 (TRICKY).
  4. Good HAProxy and Nginx config (SIMPLE).
  5. Some way to determine if you are using HTTP/2. HTTP/2 and SPDY indicator is a good one for Chrome browser.

The OpenSSL part is a bit more tricky only because most Linux distributions come with OpenSSL 1.0.1 (or older) which doesn't support ALPN (Application Layer Protocol Negotiation). ALPN extension allows the application layer to negotiate which protocol will be used in the connection, and it is essential if we want to support HTTP/2 and HTTP/1.1 on the same TCP port. Besides, HTTP/2 in HAProxy is only supported using ALPN, so it is a must be on our list.

If you are familiar with the installation process, skip it and move to the configuration section.

1. Obtain SSL certificates

You can obtain trusted certificates cheaply on ssl2buy.com, which is re-seller of many trusted issuers. I did buy a couple of certificates from them and I can recommend their service and customer support. You can get AlphaSSL certificate for under $20.

If you need to generate dummy certificates for HAProxy and/or Nginx, you can use the following commands:

We use generated by above commands certificates and keys in the configs below.

2. Nginx setup

Installing Nginx 1.9 on CentOS 7 is fairly simple. The only thing is to use mainline YUM repository, not the stable one. As described on Nginx.org page, put yum repo config in /etc/yum.repos.d/nginx.repo and do yum install:

That's it. 

Let's create Nginx vhost.conf to make sure our Nginx works as expected and we have HTTP/2 on it. Here's simple vhost config:

  • Note 1: The key is line with listen 443 default_server ssl http2. That essentially gives you HTTP/2.
  • Note 2: Ignore for now about line 3 in this config with listen 81 part - we'll come back to it soon.
  • Note 3: I use standard 80/443 ports as I run this example inside Docker image, so they do not conflict with any ports on my host machine. If needed, adjust to your case.
  • Note 4: I use a dummy.crt and dummy.key generated in Obtain SSL certificates step.

Now, when you connect to it using https:// protocol, HTTP/2 indicator should tell you that site is running on HTTP/2 protocol.

Congratulations, you have working Nginx with HTTP/2!

3. OpenSSL and HAProxy installation

This part is a bit more tricky. We need to compile OpenSSL 1.0.2 from the source (because it is not available in the yum repository yet) and then re-compile HAProxy to use it.

The way it worked for me was to build OpenSSL with the no-shared param and to link OpenSSL statically against HAProxy. I followed instructions from official HAProxy README. Funny enough, that was the last place I looked into after trying different ways... and the most resourceful. How often you read these long and often boring README files?

After that, you should have HAProxy compiled and installed. Test it with:

haproxy -vv

4. Configuration

This is the complete /etc/haproxy/haproxy.cfg we will use:

The most essential bits are here:

Here we define HTTPS front-end interface to which clients connect with HAProxy listening on port 443.

Connections are handled by backends nodes-http2 and nodes-http, depending if the client supports HTTP/2 or not. Note that we terminate (i.e., decrypt) SSL on HAProxy with this config; connections to backend servers are unencrypted. Our backend server can be reached by HAProxy by web.server hostname (which is our running Nginx, as described above).

Note bind *:443 line with alpn h2,http/1.1 where we advertise two supported protocols for clients: HTTP/2 and HTTP/1.1. This way browsers which do not support HTTP/2 yet, are still able to connect to our website

Line use_backend nodes-http2 if { ssl_fc_alpn -i h2 } redirects clients supporting HTTP/2 to nodes-http2 backend, the rest will be handled by nodes-http which uses old HTTP/1.1 protocol. This point is quite important, as you want to have backward compatibility for clients not supporting HTTP/2 yet.

So then we have a line:
server node1 web.server:81 check send-proxy
At this point, HAProxy is talking HTTP/2 protocol only. And it is connecting to web.server on unusual port 81. What pleasant surprises do we have there?

Let's use Nginx with the following vhost config (as described above):

Line:
listen      81  default_server http2 proxy_protocol;
defines server on port 81 which talks in HTTP/2 language too. Note that we cannot use the server on port 443 which talks in SSL: our SSL connection got decrypted by HAProxy and now we have a non-encrypted connection. Therefore we need separate server on its own port (81 in our case) which communicate in HTTP/2 layer only, without SSL

Small digression: there's also proxy_protocol keyword there. Its equivalent in haproxy.cfg is send-proxy, on the backend server configuration. Proxy Protocol is a separate story, well explained in this article on Nginx.com. In short, it allows passing information about client IP address and port numbers by HAProxy to the backend server (and thus: your application), which is usually very desired.

You can run HAProxy with the above config using:

haproxy -f /etc/haproxy/haproxy.cfg

Now you should be able to connect to your proxy host (e.g. https://localhost:443/) and see it working with HTTP/2. If you testing in Firefox, check network inspector headers, you should see something like X-Firefox-Spdy: "h2".

Docker images

If you live already on Docker island, you can use our MILLION12 images. Here at MILLION12 we started using Docker long before it was stable 1.0 and since then we've had pleasure to built a couple of useful images, incl. million12/haproxy and million12/nginx, which we use in this example. They already have the discussed configuration.

You can launch the whole stack using the following docker-compose.yml file. Note that we link the Nginx server under name web.server inside haproxy container, which is the hostname used in haproxy.cfg presented above.

Connect to https://haproxy:8443 and you should see screens like that (note the blue HTTP/2 indicator):

If you want to check real production project with these Docker images and the above configuration, check out https://PrototypeBrewery.io. Prototype Brewery is our product, prototyping tool for planning and building interactive and responsive web projects. Check it out, we are already on HTTP/2 (and don't forget to sign up).

Summary

As you can see, migrating to HTTP/2 is a fairly simple process which you can put in place today. There's no reason to wait as the majority of browsers supports it. Moreover, with fallback to HTTP/1.1 you are on the safe side.

If you think I missed something here, anything can be improved etc., please write in comments below.

Update: there's also a discussion on Hacker News for this post.


About the author

Marcin Ryzycki (@ryzmen) is co-founder at Prototype Brewery. He dreams of building successful startups, likes martial arts and has spent the last 15 years creating more or less awesome websites and web applications.