Nginx HTTP/2 Protocol

HTTP/2 protocol setup and configuration for Centmin Mod LEMP stack. Documenting the transition from Google SPDY to HTTP/2 in Nginx 1.9.5+.

Originally published July 2015 · Author: George Liu (eva2000)

Historical Reference — HTTP/2 Transition (2015)

This page documents the original transition from Google SPDY to HTTP/2 protocol in Nginx 1.9.5 (2015). HTTP/2 is now the standard protocol in all modern Nginx versions and is enabled by default in Centmin Mod. For current Nginx documentation, see the Nginx documentation page.

Nginx HTTP/2 Protocol to Replace Google SPDY

Centmin Mod 1.2.3-eva2000.09 beta (123.09beta01 branch) added support for Nginx’s new HTTP/2 alpha patch (nginx.com) which supports h2 Application Layer Protocol Negotiation (ALPN) TLS extensions. Installing the Nginx HTTP/2 patch or final releases will remove SPDY module support in Nginx. Unfortunately, the HTTP/2 patch or first production release will not support HTTP/2 Server Push feature but future versions may. The Nginx HTTP/2 patch was cosponsored by Dropbox and Automattic. HTTP/2 protocol will be replacing Google’s SPDY protocol in early 2016.

Backported Nginx HTTP/2 Support

Nginx 1.9.5 seems to be the version which officially support Nginx HTTP/2. Even the official Nginx HTTP/2 documentation is already in place. For Centmin Mod Nginx, Nginx HTTP/2 supported routines have been backported from Centmin Mod 1.2.3-eva2000.09 beta to 1.2.3-eva2000.08 stable branch as at September 18th, 2015. So ensure you update Centmin Mod code as outlined on Upgrade page before updating to Nginx 1.9.5 via centmin.sh menu option 4.

Nginx HTTP/2 Patch Updates

Nginx HTTP/2 patch version updates can be found at nginx.org/patches/http2/. Centmin Mod 1.2.3-eva2000.09 beta will automatically apply the latest Nginx HTTP/2 patch when you run centmin.sh menu option 4 to upgrade/recompile Nginx 1.9.3+ and higher versions.

2015-09-11 Version 6 (NGINX 1.9.4)
 - Sending GOAWAY frame on connection shutdown;
 - Proper handling of huge response headers using CONTINUATION frames;
 - Various minor fixes.

2015-09-08 Version 5 (NGINX 1.9.4)
 - Validation of the request headers;
 - Improved error handling even more;
 - Added the http2_max_header_size directive that limits the maximum size of
   a request headers (16384 bytes by default);
 - Fixed the default value of the http2_max_field_size directive;
 - Limited consumption of memory during the HTTP/2 session;
 - The $request_line variable for HTTP/2 requests now uses "HTTP/2.0" as
   the version token instead of "HTTP/1.1".

2015-08-31 Version 4 (NGINX 1.9.4)
 - Improved error handling;
 - Added the http2_max_field_size directive that limits the maximum size of
   a request header field (4096 bytes by default);
 - The http2_keepalive_timeout directive was renamed to http2_idle_timeout.

2015-08-19 Version 3 (NGINX 1.9.4)
 - The gzip and gzip_static modules might not work with HTTP/2 requests;
 - Fixed processing of Cookie headers.

2015-08-14 Version 2 (NGINX 1.9.3)
 - Introduced NPN support for HTTP/2 negotiation (this reduces OpenSSL version
   requirement to 1.0.1+);
 - Various fixes in the prioritization mechanism;
 - Fixed missing "Location" response header in some configurations;
 - Fixed processing of the ":authority" pseudo-header (the HTTP/2 analog of
   the "Host" header).

2015-08-05 Version 1 (NGINX 1.9.3)
The first public alpha release.

Nginx HTTP/2 vs Nginx SPDY

After applying Nginx HTTP/2 patch or updating to Nginx 1.9.5+ and higher for HTTP/2 support, Nginx SPDY module will be removed. This means there are a few technical changes to your Nginx vhost configurations you need to make for HTTP/2 to work.

Listen Directive Changes

Replace spdy with http2 parameter in the listen directive:

Before (SPDY):

listen 443 ssl spdy;

After (HTTP/2):

listen 443 ssl http2;

If you do not replace the listen directive and have Nginx HTTP/2 compiled, you will receive an error on Nginx restart:

ngxrestart
nginx: [emerg] invalid parameter "spdy": the SPDY module was deprecated, use the HTTP/2 module instead in /usr/local/nginx/conf/conf.d/newdomain.com.ssl.conf:15
nginx: configuration file /usr/local/nginx/conf/nginx.conf test failed

Removed SPDY Options

Some SPDY-specific options will need to be removed for Nginx HTTP/2 patched server to start up properly. These options include:

Error on Nginx restart after HTTP/2 patch applied for unsupported SPDY options:

nginx: [emerg] unknown directive "spdy_keepalive_timeout"

HTTP/2 Configuration Directives

There are optional HTTP/2 configuration directives for Nginx:

  • http2_recv_buffer_size (http) — specifies the size of input buffer per worker (256k by default)
  • http2_max_concurrent_streams (http, server) — sets the maximum number of concurrent HTTP/2 streams in a single connection (128 by default)
  • http2_streams_index_size (http, server) — configures the size of the HTTP/2 stream ID index; must be a power of 2 (default is 32)
  • http2_recv_timeout (http, server) — defines the timeout when expecting more data from the client (default is 30s)
  • http2_keepalive_timeout (http, server) — sets the inactivity timeout after which the connection is closed (default is 3m)
  • http2_chunk_size (http, server, location) — defines the default DATA frames size for response body (default is 8k)

HTTP/2 Variable

  • $http2 — negotiated protocol identifier if the request came via HTTP/2 (“h2”, “h2c”), or empty value

HTTP/2 Browser Compatibility

HTTP/2 browser compatibility wasn’t as widespread compared to SPDY at the time of these tests, according to information at caniuse.com.

SPDY Browser Compatibility

SPDY browser compatibility chart from caniuse.com showing support across Chrome, Firefox, Opera, and other browsers

HTTP/2 Browser Compatibility

HTTP/2 browser compatibility chart from caniuse.com showing support across Chrome, Firefox, Edge, Safari, and other browsers

Nginx HTTP/2 Tools

For command line testing for HTTP/2 sites, some of the tools like h2spec, nghttp2, h2i, cipherscan, ssllabs-scan, testssl, curl with --http2 support, and openssl 1.0.2 with chacha20_poly1305 cipher support are bundled into the Docker Ubuntu Vivid nghttp2 container bundle. Included in nghttp2 is a HTTP/2 and SPDY supported benchmarking load testing tool h2load.

Nginx HTTP/2 Benchmarks

Below tests were done with Nginx HTTP/2 version 1 of the alpha patch. Nginx HTTP/2 version 2 of alpha patch has just been released and all centminmod.com servers have been updated to Nginx HTTP/2 version 2 patch. It seems that Nginx HTTP/2 version 2 of the patch has also fixed some of the Nginx HTTP/2 + ngx_pagespeed issues and now ngx_pagespeed works. However, webpagetest.org does still report Error: Timed Out either on First View or Repeat View tests stage. You can find Nginx HTTP/2 version 2 patch benchmark results here.

2015-08-14 Version 2 (NGINX 1.9.3)
 - Introduced NPN support for HTTP/2 negotiation (this reduces OpenSSL version
   requirement to 1.0.1+);
 - Various fixes in the prioritization mechanism;
 - Fixed missing "Location" response header in some configurations;
 - Fixed processing of the ":authority" pseudo-header (the HTTP/2 analog of
   the "Host" header).

2015-08-05 Version 1 (NGINX 1.9.3)
The first public alpha release.

Working Nginx HTTP/2 + ngx_pagespeed

nghttp -nas https://centminmod.com:443
***** Statistics *****

Request timing:
  responseEnd: the  time  when  last  byte of  response  was  received
               relative to connectEnd
 requestStart: the time  just before  first byte  of request  was sent
               relative  to connectEnd.   If  '*' is  shown, this  was
               pushed by server.
      process: responseEnd - requestStart
         code: HTTP status code
         size: number  of  bytes  received as  response  body  without
               inflation.
          URI: request URI

see http://www.w3.org/TR/resource-timing/#processing-model

sorted by 'complete'

id  responseEnd requestStart  process code size request path
 13    +80.22ms       +532us  79.69ms  200  29K /
 15    +91.08ms     +66.98ms  24.10ms  200   9K /img/favicon.ico
 17   +125.17ms     +66.99ms  58.18ms  200  60K /css/A.localfonts.css+font-awesome.min.css+...
 21   +141.41ms     +66.99ms  74.42ms  200   7K /js/hover-dropdown-menu.js+...
 25   +156.83ms     +67.03ms  89.81ms  200   6K /js/custom.js.pagespeed.jm...
 19   +157.11ms     +66.99ms  90.11ms  200  38K /js/jquery.min.js+bootstrap.min.js...
 23   +157.12ms     +67.02ms  90.09ms  200  24K /js/bootstrapValidator.min.js...

Centmin Mod Nginx Compile Options with --with-http_v2_module

nginx -V
nginx version: nginx/1.9.3
built by clang 3.4.2 (tags/RELEASE_34/dot2-final)
built with LibreSSL 2.2.2
TLS SNI support enabled
configure arguments: --with-ld-opt='-lrt -ljemalloc -Wl,-z,relro -Wl,-rpath,/usr/local/lib' --with-cc-opt='...' --sbin-path=/usr/local/sbin/nginx --conf-path=/usr/local/nginx/conf/nginx.conf --with-http_ssl_module --with-http_v2_module --with-http_gzip_static_module --with-http_stub_status_module --with-http_sub_module --with-http_addition_module --with-http_image_filter_module --with-http_secure_link_module --with-http_realip_module --with-http_geoip_module --with-openssl-opt=enable-tlsext --add-module=../ngx-fancyindex-ngx-fancyindex --add-module=../ngx_cache_purge-2.3 --add-module=../nginx-accesskey-2.0.3 --add-module=../nginx-http-concat-master --add-module=../openresty-memc-nginx-module --add-module=../openresty-srcache-nginx-module --add-module=../ngx_devel_kit-0.2.19 --add-module=../set-misc-nginx-module-0.29 --add-module=../echo-nginx-module-0.58 --add-module=../redis2-nginx-module-0.12 --add-module=../ngx_http_redis-0.3.7 --add-module=../lua-nginx-module-0.9.16 --add-module=../lua-upstream-nginx-module-0.03 --add-module=../lua-upstream-cache-nginx-module-0.1.1 --add-module=../nginx_upstream_check_module-0.3.0 --add-module=../nginx-module-vts --add-module=../headers-more-nginx-module-0.261 --with-openssl=../libressl-2.2.2 --with-libatomic --with-threads --with-stream --with-stream_ssl_module --with-pcre=../pcre-8.37 --with-pcre-jit --add-module=../ngx_pagespeed-release-1.9.32.6-beta

Benchmark Test Links

Nginx HTTP/2 World Flags Demo

A Nginx HTTP/2 World Flags Demo page was created so folks could compare both Nginx HTTP/2 vs non-HTTP/2 performance. Note for Nginx HTTP/2 + ngx_pagespeed enabled (which was broken), webpagetest.org test first view results in a timeout eventually, possibly related to ngx_pagespeed not working with Nginx HTTP/2 patch. An extended test with Nginx HTTP/2 vs Nginx SPDY/3.1 vs Nginx HTTP/1.1 was also added.

Test Configurations

  • HTTP/1.1 non-https + ngx_pagespeed enabled
  • HTTP/1.1 non-https + ngx_pagespeed disabled
  • HTTP/2 https + ngx_pagespeed enabled but broken
  • HTTP/2 https + ngx_pagespeed disabled

4-way WebPageTest.org comparison from Los Angeles location with Chrome 3G Mobile 300ms RTT connection speed and 1920x1200 resolution window. All 3rd party scripts were blocked for testing purposes.

4-way Nginx HTTP/2 WebPageTest comparison showing HTTP/1.1 with and without ngx_pagespeed vs HTTP/2 with and without ngx_pagespeed

The Nginx HTTP/2 vs non-HTTP/2 results with ngx_pagespeed confirmed the same results when testing h2o HTTP/2 and OpenLiteSpeed HTTP/2 World Country Flags tests. Nginx HTTP/1.1 + ngx_pagespeed was still the fastest combination, then Nginx HTTP/2 + ngx_pagespeed (even if it was broken), then Nginx HTTP/2 and slowest by far was Nginx HTTP/1.1 with ngx_pagespeed disabled.

WebPageTest summary for HTTP/1.1 with ngx_pagespeed enabled WebPageTest summary for HTTP/1.1 with ngx_pagespeed disabled WebPageTest summary for HTTP/2 with ngx_pagespeed enabled WebPageTest summary for HTTP/2 with ngx_pagespeed disabled

Filmstrip Comparisons

WebPageTest filmstrip comparison of HTTP/2 vs HTTP/1.1 page load progression WebPageTest filmstrip comparison continued - rendering progression WebPageTest filmstrip comparison continued - visual completeness WebPageTest filmstrip comparison continued - load completion WebPageTest filmstrip detail - HTTP/1.1 with ngx_pagespeed WebPageTest filmstrip detail - HTTP/1.1 without ngx_pagespeed WebPageTest filmstrip detail - HTTP/2 with ngx_pagespeed WebPageTest filmstrip detail - HTTP/2 without ngx_pagespeed

Testing Nginx HTTP/2

Both centminmod.com and sslspdy.com sites had their Centmin Mod LEMP versions updated to 123.09beta01 branch with Nginx 1.9.3 patched with HTTP/2 support. You can check out both site’s HTTP/2 implementations on the https:// version of the sites:

There are Chrome and Firefox extensions and plugins for HTTP/2 & SPDY Indicators you can install.

Chrome HTTP/2 indicator showing h2 protocol for centminmod.com

You can also use Chrome or Firefox Developer Tools to enable the protocol display field in Network tab.

Chrome Developer Tools Network tab showing h2 protocol column for HTTP/2 requests

Checking ALPN Protocol

Checking the ALPN protocol returns h2 for HTTP/2:

openssl s_client -alpn h2 -host centminmod.com -port 443
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-CHACHA20-POLY1305
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
ALPN protocol: h2
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-CHACHA20-POLY1305
openssl s_client -alpn h2 -host sslspdy.com -port 443
New, TLSv1/SSLv3, Cipher is ECDHE-ECDSA-CHACHA20-POLY1305
Server public key is 256 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
ALPN protocol: h2
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-ECDSA-CHACHA20-POLY1305

Using h2i Tool

h2i centminmod.com
Connecting to centminmod.com:443 ...
Connected to 104.152.214.227:443
Negotiated protocol "h2"
[FrameHeader SETTINGS len=18]
  [MAX_CONCURRENT_STREAMS = 128]
  [INITIAL_WINDOW_SIZE = 2147483647]
  [MAX_FRAME_SIZE = 16777215]
[FrameHeader WINDOW_UPDATE len=4]
  Window-Increment = 2147418112
h2i sslspdy.com
Connecting to sslspdy.com:443 ...
Connected to 192.184.89.66:443
Negotiated protocol "h2"
[FrameHeader SETTINGS len=18]
  [MAX_CONCURRENT_STREAMS = 128]
  [INITIAL_WINDOW_SIZE = 2147483647]
  [MAX_FRAME_SIZE = 16777215]
[FrameHeader WINDOW_UPDATE len=4]
  Window-Increment = 2147418112

Using curl with --http2

Using custom compiled curl 7.43+ with nghttp2 support which allows the curl --http2 flag option to test over HTTP/2 connection:

curl -I --http2 https://centminmod.com -v
* Rebuilt URL to: https://centminmod.com/
*   Trying 104.152.214.227...
* Connected to centminmod.com (104.152.214.227) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1

* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Using Stream ID: 1 (easy handle 0x117f5a0)
> HEAD / HTTP/1.1
> Host: centminmod.com
> User-Agent: curl/7.43.0-DEV
> Accept: */*
>
* http2_recv: 16384 bytes buffer at 0x117fef0 (stream 1)
* Connection state changed (MAX_CONCURRENT_STREAMS updated)!
* http2_recv: 16384 bytes buffer at 0x117fef0 (stream 1)
* http2_recv: 16384 bytes buffer at 0x117fef0 (stream 1)
* http2_recv: returns 247 for stream 1
< HTTP/2.0 200
HTTP/2.0 200

Using nghttp2 Tool

Checking HTTP/2 via nghttp2 tool itself over HTTP/2 connection:

nghttp -nv https://centminmod.com:443
[  0.063] Connected
The negotiated protocol: h2
[  0.097] recv SETTINGS frame <length=18, flags="0x00," stream_id="0">
          (niv=3)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):128]
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):2147483647]
          [SETTINGS_MAX_FRAME_SIZE(0x05):16777215]

nghttp Stats

nghttp -nas https://centminmod.com:443
***** Statistics *****

Request timing:
  responseEnd: the  time  when  last  byte of  response  was  received
               relative to connectEnd
 requestStart: the time  just before  first byte  of request  was sent
               relative  to connectEnd.   If  '*' is  shown, this  was
               pushed by server.
      process: responseEnd - requestStart
         code: HTTP status code
         size: number  of  bytes  received as  response  body  without
               inflation.
          URI: request URI

see http://www.w3.org/TR/resource-timing/#processing-model

sorted by 'complete'

id  responseEnd requestStart  process code size request path
 13    +35.99ms       +316us  35.67ms  200  10K /
 15    +45.66ms     +36.04ms   9.62ms  200   9K /img/favicon.ico
 17    +46.15ms     +36.05ms  10.10ms  200   1K /css/localfonts.css
 19    +54.87ms     +36.05ms  18.82ms  200   5K /css/font-awesome.min.css
 21    +63.82ms     +36.05ms  27.77ms  200  19K /css/bootstrap.min.css
 23    +64.07ms     +36.05ms  28.01ms  200   4K /css/hover-dropdown-menu.css
 25    +64.12ms     +36.05ms  28.06ms  200  569 /css/icons-set8.css
 27    +70.11ms     +36.06ms  34.05ms  200   3K /css/animate.min.css
 29    +79.33ms     +36.06ms  43.27ms  200  24K /css/style.css
 31    +79.51ms     +36.06ms  43.45ms  200   2K /css/responsive.css
 33    +79.56ms     +36.07ms  43.49ms  200   1K /css/color.css
 35    +88.28ms     +36.07ms  52.21ms  200  29K /js/jquery.min.js
 37    +88.81ms     +36.07ms  52.74ms  200   9K /js/bootstrap.min.js
 39    +89.04ms     +36.07ms  52.96ms  200   6K /js/hover-dropdown-menu.js
 41    +89.07ms     +36.07ms  52.99ms  200  985 /js/jquery.hover-dropdown-menu-addon.js
 43    +89.10ms     +36.07ms  53.02ms  200  797 /js/jquery.easing.1.3.js
 47    +92.29ms     +36.08ms  56.22ms  200   8K /js/custom.js
 45   +101.77ms     +36.08ms  65.69ms  200  25K /js/bootstrapValidator.min.js

WebPageTest.org Testing Nginx HTTP/2

Webpagetest.org comparison test for centminmod.com for testing HTTP/1.1 + ngx_pagespeed, HTTP/1.1 + ngx_pagespeed disabled and HTTP/2 without ngx_pagespeed. This site probably isn’t the best example for HTTP/2 or ngx_pagespeed usage due to minimalistic design. The more page elements — JS, CSS, and images the page has, the greater the benefit HTTP/2 and SPDY would have.

HTTP/1.1 + ngx_pagespeed disabled:

WebPageTest summary for centminmod.com HTTP/1.1 with ngx_pagespeed disabled

HTTP/1.1 + ngx_pagespeed enabled:

WebPageTest summary for centminmod.com HTTP/1.1 with ngx_pagespeed enabled

HTTP/2:

WebPageTest summary for centminmod.com HTTP/2

3-Way Comparison Filmstrips

WebPageTest 3-way comparison of centminmod.com HTTP/1.1, HTTP/1.1+PageSpeed, HTTP/2 WebPageTest 3-way comparison filmstrip continued WebPageTest 3-way comparison filmstrip continued WebPageTest 3-way comparison filmstrip continued WebPageTest 3-way comparison filmstrip final

Looking for Current Nginx Documentation?

This page is a historical reference for the HTTP/2 transition from SPDY. HTTP/2 is now standard in all modern Nginx versions and Centmin Mod. For up-to-date Nginx configuration, SSL/TLS setup, and performance tuning:

View Nginx Documentation