Nginx configured with http2 doesn't deliver HTTP/2

I've just run into the same problem, but I think I know why it happens. nginx 1.9.6 is not a stock package on Ubuntu 14.04, so you're probably getting it from an nginx PPA. That's fine, but those packages are built with the stock libraries from 14.04, which is to say OpenSSL 1.0.1f. Unfortunately that version of OpenSSL does not contain RFC7301 ALPN support which is needed for proper HTTP/2 negotiation; it only supports the now-deprecated NPN. It looks like Chrome has already removed support for NPN, so it's incapable of negotiating an HTTP/2 connection without ALPN. Firefox 41 on the other hand, still has NPN support and you should be able to use HTTP/2 with that.

You can test your server like this - you will need OpenSSL 1.0.2d installed on your client (run openssl version to check):

Test with ALPN:

echo | openssl s_client -alpn h2 -connect yourserver.example.com:443 | grep ALPN

If ALPN is working, you should see:

ALPN protocol: h2

otherwise you'll get:

No ALPN negotiated

Test with NPN:

echo | openssl s_client -nextprotoneg h2 -connect yourserver.example.com:443

If that works, you will get:

Next protocol: (1) h2
No ALPN negotiated

That means that it's successfully negotiating an HTTP/2 connection via NPN, which is what Firefox does.

So how to solve this? The only way I can see is to install a later build of openssl from a PPA (I use this one for PHP, which also contains openssl) and build your own nginx linked to it. You can find the config params for your existing nginx build by running nginx -V, and you should be able to use that to build your own version.

Update: I've discovered that the reason that Chrome doesn't support HTTP/2 with NPN is not that it doesn't support NPN (though it will be dropped at some point), but that it specifically doesn't support h2 with NPN, as shown on the chrome://net-internals/#http2 page:

Chrome HTTP/2 info


Short version.

I found that ESET antivirus can prevent HTTP/2 from working when SSL/TLS filtering is turned on, on the browsing computer. Check to see that your antivirus is not filtering the SSL/TLS.


TLDR version

I ran into the same problem as the poster, but with an interesting twist. I upgraded my server configuration to nginx 1.12.1. compiled with OpenSSL 1.0.2.g and on initial inspection it had “solved” the problem of HTTP/2 not working. In my browser, I could see that the server certificate was verified by Let’s Encrypt. Content was also being served with HTTP/2.

Some time later, I found that the very same page and same resources were no longer being served over HTTP/2. Coincidentally, the site was no longer verified by Let’s Encrypt, but by Eset?!!?! To my amazement, the new http2 problem had nothing to do with my server configuration at all. It turned out that I had SSL/TLS filtering on in my antivirus on my local computer and this was causing the issue. The solution was to turn off SSL/TLS filtering in the antivirus. Once I had turned it off (and rebooted the computer) HTTP/2 worked again and the certificate was verified by Let’s Encrypt again.

For instructions on how to turn off SSL/TLS in ESET, check out http://support.eset.com/kb3126/?locale=en_US