nginx配置HTTP/2 Server Push

在HTTP/2规范中,引入了一项新的技术——Server Push,允许服务器实现提前推送响应。用一个通俗的例子来讲,就是在浏览器请求HTML时,服务器在响应HTML的同时把层叠样式表和脚本等静态资源便一并推送给客户端,减少了连接数和请求数,降低了响应时间。推荐参看饿了么前端团队在知乎发表的这篇文章《浅谈 HTTP/2 Server Push》。
服务器程序nginx在1.13.9版本中,终于官方支持了这一特性。要想启用Server Push,必须先启用SSL和HTTP/2。下面是一个简单示例:

    location = /demo.html {
        http2_push /style.css;
        http2_push /image1.jpg;
        http2_push /image2.jpg;
    }

当浏览器请求/demo.html时,nginx便会把/style.css/image1.jpg以及/image2.jpg推送给浏览器缓存。当浏览器解析HTML遇到这些资源时,便会从缓存中找到这些资源加载,而不是去向服务器请求。
Chrome的开发者工具中,可以看到这些静态资源由服务器推送而来:
Chrome DevTool
上面那个示例中的配置方式十分不方便,但我们有简单的方式——通过设置HTTP Header来实现Server Push,需要在nginx配置中开启http2_push_preload,示例如下

location = /myapp {
    proxy_pass http://upstream;
    http2_push_preload on;
}

然后在上游HTTP服务中增添以下HTTP Header即可令nginx推送/style.css/favicon.ico

Link: </style.css>; as=style; rel=preload, </favicon.ico>; as=image; rel=preload

但是上述配置的方式有一个缺点,那就是不管你需不需要这些静态资源,服务器君都会推送它们,使得浏览器本地缓存失效,造成浪费。好在我们可以利用一个小技巧去解决它:服务器在推送的同时设置一个Cookie,当浏览器下次请求时,服务器先检查该Cookie是否存在,若存在就不推送,若不存在就推送,配置示例如下:

server {
    listen 443 ssl http2;
    server_name example.com

    ssl_certificate ssl/certificate.pem;
    ssl_certificate_key ssl/key.pem;

    root /var/www/html;
    http2_push_preload on;

    location = /demo.html {
        add_header Set-Cookie "session=1";
        add_header Link $resources;
    }
}

map $http_cookie $resources {
    "~*session=1" "";
    default "</style.css>; as=style; rel=preload, </image.jpg>; as=image; rel=preload, </script.jpg>; as=script; rel=preload";
}

更详细的说明和性能对比请参看nginx官方博客