Nginx has recently implemented HTTP/2 server push. This feature allows Nginx to push content to frontend browser without/before the browser requesting for the content.
In usual page loading, the browser will be repeating GET > RECEIVE > GET > RECEIVE > GET > RECEIVE. With HTTP/2 server push, it reduces GET for all the push contents, which results in a sequence similar to GET > RECEIVE > RECEIVE PUSH > RECEIVE PUSH > GET > RECEIVE. This feature will actually improve the loading time because Round Trip Time (RTT) is reduced.
Steps to enable this feature:
(1) Install Nginx v1.13.9 or later. (At this time, this version is in the mainline repository)
(2) There are 2 ways to enable this feature.
(a) Static way - By specifying the content to push in your http block.
server { location = / { http2_push /main.css; } }
(b) Dynamic way - By asking Nginx to intercept response and check for a specific header.
server { location = / { http2_push_preload on; } }
When this is set, Nginx will intercept "Link" header. You can specify the content to push in this header. E.g.
Link "</main.css>; as=style; rel=preload" Link "</main.js>; as=script; rel=preload" Link "</background.jpg>; as=image; rel=preload"
These headers can be set in upstream server or directly from your application.
(3) Make sure your have enabled http2 feature.
server { listen 443 ssl http2; }
(4) Reload Nginx and test.
(5) You can observe the push behavior via chrome://net-internals/#events
t=7272 [st= 1] HTTP2_SESSION_RECV_PUSH_PROMISE --> :method: GET :path: /main.css :scheme: https :authority: 192.168.x.x accept-encoding: gzip, deflate, br accept-language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7,ms;q=0.6,zh-TW;q=0.5 user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36 --> id = 31 --> promised_stream_id = 18 t=7272 [st= 1] HTTP2_STREAM_SEND_PRIORITY --> exclusive = true --> parent_stream_id = 31 --> stream_id = 18 --> weight = 110
(6) If the push content is already loaded or is cached, client will reject the push hint. You will see something like this:
t= 4235 [st= 1] HTTP2_SESSION_RECV_PUSH_PROMISE --> :method: GET :path: /main.css :scheme: https :authority: 192.168.x.x accept-encoding: gzip, deflate, br accept-language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7,ms;q=0.6,zh-TW;q=0.5 user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36 --> id = 25 --> promised_stream_id = 14 t= 4235 [st= 1] HTTP2_SESSION_SEND_RST_STREAM --> description = "Duplicate pushed stream with url: https://192.168.x.x/main.css" --> error_code = "7 (REFUSED_STREAM)" --> stream_id = 14
Note 1: This feature is available in Nginx current mainline 1.13.9+. Which means that it is not yet supported by custom build such as Passenger version of Nginx.
Note 2: Supported browser can be found here: https://caniuse.com/#feat=http2