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