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 here. 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 with replacing spdy with http2 parameter

  listen 443 ssl spdy;

becomes

  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 similar to the one below.

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

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"

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 isn't as wide spread compared to SPDY right now according to information at caniuse.com.

SPDY Browser Compatibility

HTTP/2 Browser Compatibility


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, openssl 1.0.2 with chacha20_poly1305 cipher support are bundled into my 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 my 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+bootstrap.min.css+hover-dropdown-menu.css+icons-set8.css+animate.min.css+style.css+responsive.css+color.css,Mcc.FHj-r4A0cI.css.pagespeed.cf.YOn5Xvomna.css
 21   +141.41ms     +66.99ms  74.42ms  200   7K /js/hover-dropdown-menu.js+jquery.hover-dropdown-menu-addon.js+jquery.easing.1.3.js.pagespeed.jc.vy5S6wKQse.js
 25   +156.83ms     +67.03ms  89.81ms  200   6K /js/custom.js.pagespeed.jm.q-StvNlmtR.js
 19   +157.11ms     +66.99ms  90.11ms  200  38K /js/jquery.min.js+bootstrap.min.js.pagespeed.jc.Cd39AMnoIp.js
 23   +157.12ms     +67.02ms  90.09ms  200  24K /js/bootstrapValidator.min.js.pagespeed.jm.YU3KUlvaHb.js                                 

Centmin Mod 123.09beta01 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='-m64 -mtune=native -g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wno-deprecated-declarations -Wno-unused-parameter -Wno-unused-const-variable -Wno-conditional-uninitialized -Wno-mismatched-tags -Wno-c++11-extensions -Wno-sometimes-uninitialized -Wno-parentheses-equality -Wno-tautological-compare -Wno-self-assign -Wno-deprecated-register -Wno-deprecated -Wno-invalid-source-encoding -Wno-pointer-sign -Wno-parentheses -Wno-enum-conversion' --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-4f6f78f --add-module=../openresty-srcache-nginx-module-ffa9ab7 --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


Nginx HTTP/2 World Flags Demo

I've recreated a Nginx HTTP/2 World Flags Demo page so folks can check out and compare both Nginx HTTP/2 vs non-HTTP/2 performance. Note for Nginx HTTP/2 + ngx_pagespeed enabled (which is broken) webpagetest.org test first view results in a timeout eventually, so could be related to ngx_pagespeed not working with Nginx HTTP/2 patch. Update: also added a Nginx HTTP/2 World Flags Demo extended test with Nginx HTTP/2 vs Nginx SPDY/31 vs Nginx HTTP/1.1.

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 webpagetets.org test

The Nginx HTTP/2 vs non-HTTP/2 results with ngx_pagespeed confirmed the same results I had when I tested 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.


Testing Nginx HTTP/2

Both centminmod.com site and sslspdy.com sitess have 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 implements on the https:// version of the sites:

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

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

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 to check.

  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 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

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 
          (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. Just bare that in mind.