It may seem trivial to enable HTTP/2 support in Nginx, but there are some caveats you may face. Let's go through the ones I faced during the HTTP/2 configuration for our sites.
Enable HTTP/2 in Nginx
To enable HTTP/2 in Nginx you just need to add a few lines to your server configuration:
server { listen 443 ssl http2; ssl_certificate <PATH TO THE *.crt file>; ssl_certificate_key <PATH TO THE *.key file>; #... }
Here you just need to configure Nginx to listen to the 443 port, which is default for SSL, and specify that this server will use SSL, and serve data via the HTTP/2 protocol. Moreover, you need to specify a path to your certificate files. That's it, pretty straightforward.
Caveat 1: Invalid parameter "http2"
If you added the lines above to your configuration, reloaded Nginx and got such an error, then the first thing you need to check is the version of your Nginx installation:
nginx -v nginx version: nginx/1.11.1
As I described in one of my previous articles about Varnish, Nginx and HTTP/2, you must install Nginx of at least the 1.9.5
version and compile it with the http2
module or our configuration line
listen 443 ssl http2;
won't work and you'll get a critical error.
Caveat 2: Nginx still serves data via HTTP/1.1 (in Chrome)
Here you have a few options: you can go directly to the solution or read more and understand the nature of this problem.
Ok, you've installed a proper version with the http2 module, but, how can you be sure that it works properly and Nginx serves data via HTTP/2?
There are many ways and tools:
HTTP/2 Online Test
The easiest way to check that your server is OK is via the http2 online tool. Just enter the URL of your site and get the results:
Pay attention to the ALPN section. It must be enabled or HTTP/2 won't work in Chrome.
Test HTTP/2 via a Chrome Extension
This option is good if you want to always know if the site supports HTTP/2 or not. You can install the HTTP/2 and SPDY indicator
extension. It adds an indicator to the address bar that's blue if the page is HTTP/2 enabled.
Test via Chrome Dev Tools
One more possible option is to check what's going on using Chrome Dev Tools. Just open the dev tools, choose the Network tab, open the context menu on the column header row and click Protocol
. That's it, now you have an additional column with the needed information. Just reload the page and you'll see something like:
Here we can see that content is loaded via HTTP/2.
Why HTTP/2 May not Work in Chrome
As Chrome stated in their blog post:
... starting on May 15th — the anniversary of the HTTP/2 RFC — Chrome will no longer support SPDY. Servers that do not support HTTP/2 by that time will serve Chrome requests over HTTP/1.1.
At the same time, Chrome will stop supporting the TLS protocol extension NPN, which allows servers to negotiate SPDY and HTTP/2 connections with clients. NPN has been superseded by the TLS extension ALPN, published by the IETF in 2014. ALPN is already used 99% of the time to negotiate HTTP/2 with Chrome, and the remaining servers can gain ALPN support by upgrading their SSL library
Update: To better align with Chrome's release cycle, SPDY and NPN support will be removed with the release of Chrome 51.
That means that if your web server doesn't support the TLS extension ALPN, then Chrome will use HTTP/1.1 and your clients won't see an improved user experience.
SPDY
SPDY is an open networking protocol developed primarily at Google for transporting web content. SPDY manipulates HTTP traffic, with particular goals of reducing web page load latency and improving web security. SPDY achieves reduced latency through compression, multiplexing, and prioritisation. Throughout the process, the core developers of SPDY have been involved in the development of HTTP/2. As of February 2015, Google announced that following the recent final ratification of the HTTP/2 standard, support for SPDY would be deprecated, and that support for SPDY would be withdrawn completely in 2016.
NPN
NPN, or Next Protocol Negotiation, is the predecessor of ALPN and was widely used in conjunction with SPDY. NPN is quite similar to ALPN, however, the main difference is that NPN does not include the list of supported protocols in its ClientHello message. The NPN extension is included in the ClientHello message, however, it is the server which sends back the list of protocols for the client to choose from. It's expected that a client will have a list of protocols that it supports, in preference order, and will only select a protocol if the server supports it.
ALPN
Application-Layer Protocol Negotiation (ALPN) is a Transport Layer Security (TLS) extension for application layer protocol negotiation. ALPN allows the application layer to negotiate which protocol should be performed over a secure connection in a manner which avoids additional round trips and which is independent of the application layer protocols. It is used by HTTP/2.
When multiple application protocols are supported on a single server-side port number, such as port 443, the client and the server need to negotiate an application protocol for use with each connection. It is desirable to accomplish this negotiation without adding network round-trips between the client and the server, as each round-trip will degrade an end-user's experience.
TLS handshake
With ALPN, the client sends the list of supported application protocols as part of the TLS ClientHello message. The server chooses a protocol and sends the selected protocol as part of the TLS ServerHello message. The application protocol negotiation can thus be accomplished within the TLS handshake without adding network round-trips.
It is expected that a server will have a list of protocols that it supports in preference order and will only select a protocol if the client supports it. In that case, the server should select the most highly preferred protocol that it supports and that is also advertised by the client.
Here is the list of SSL libraries and their versions from what they support ALPN (wiki):
- GnuTLS since version 3.2.0 released in May 2013.
- MatrixSSL since version 3.7.1 released in December 2014.
- Network Security Services since version 3.15.5 released in April 2014.
- OpenSSL since version 1.0.2 released in January 2015.
- LibreSSL since version 2.1.3 released in January 2015.
- mbed TLS (previously PolarSSL) since version 1.3.6 released in April 2014.
- SChannel since 8.1 / 2012 R2.
- s2n since its original public release in June 2015.
- wolfSSL (formerly CyaSSL) since version 3.7.0 released in October 2015.
As we will build Nginx with OpenSSL, we'll need to use at least the 1.0.2
version.
Solution: Nginx with OpenSSL 1.0.2 (ALPN) on CentOS 7
If you use OpenSSL, you'll need to compile Nginx with a newer version of it (check the ALPN/SSL list). We use CentOS 7, and I've found a Gist with pretty simple instructions on how you can update everything you need:
yum -y groupinstall 'Development Tools' yum -y install wget openssl-devel libxml2-devel libxslt-devel gd-devel perl-ExtUtils-Embed GeoIP-devel OPENSSL="openssl-1.0.2h" NGINX="nginx-1.11.1-1" mkdir -p /opt/lib wget https://www.openssl.org/source/$OPENSSL.tar.gz -O /opt/lib/$OPENSSL.tar.gz tar -zxvf /opt/lib/$OPENSSL.tar.gz -C /opt/lib rpm -ivh http://nginx.org/packages/mainline/centos/7/SRPMS/$NGINX.el7.ngx.src.rpm sed -i "s|--with-http_ssl_module|--with-http_ssl_module --with-openssl=/opt/lib/$OPENSSL|g" /root/rpmbuild/SPECS/nginx.spec rpmbuild -ba /root/rpmbuild/SPECS/nginx.spec rpm -Uvh /root/rpmbuild/RPMS/x86_64/$NGINX.el7.centos.ngx.x86_64.rpm
Conclusions
In my case, I accidentally noticed that Nginx was serving data via HTTP/1.1 even after HTTP/2 had been enabled, so, it's good to know potential caveats when you're trying to configure something new.