Login With Github

HTTP/2 Server Push Tutorial

The main role of HTTP/2 protocol is to improve the performance of web page.

The header transmitted text directly, while now it's compression transmission. In past, in the same TCP connection, only after the last response was sent could the server send the next one. While now it can send multiple responses together.

Server push is the only function that requires the developer himself to configure inside the HTTP/2 protocol. Other functions are automatically implemented by the server and browser.

It introduces the principle and configuration methods of server push in the article.

1. Traditional method

Here is a very simple HTML web file index.html.

<!DOCTYPE html>
  <link rel="stylesheet" href="style.css">
  <h1>hello world</h1>
  <img src="example.png">

The page contains a stylesheet style.css and an image file example.png. And the browser will make three requests in order to render the page. The first request is index.html.

GET /index.html HTTP/1.1

The server receives the request and sends index.html to the browser. The browser determines that it contains the stylesheet and image. It then makes two more requests.

GET /style.css HTTP/1.1

GET /example.png HTTP/1.1

It's the traditional web request method. But there are two problems. First, it requires at least two rounds of HTTP communication. Second, the web page will be blank before receiving the style file, and Once the time of being blank exceeds 2 seconds, the user experience will be very unfriendly.

2. Improvement of the traditional method

One solution is to consolidate external resources into web files to reduce HTTP requests. For example, put the contents of the stylesheet into the <style> tag and change the image to Data URL which is Base64-encoded.

Another solution is to preload the resource. The web page tells the browser in advance to download certain resources immediately. For example, the above example can be written as follows.

<link rel="preload" href="/styles.css" as="style">
<link rel="preload" href="/example.png" as="image">

For the above example, the preload command doesn't help. However, if the previous web page uses the command to preload the resources which will be needed for the next web page, then the user will feel fast when he opens the next web page.

Both of the two methods have disadvantages. Although the first method reduces HTTP requests, it merges different types of code into one file, which is not conducive to code optimization. The second method just downloads ahead of time and doesn't reduce the HTTP requests.

3. Server push

The role of server push is that the server pushes various resources to the browser though the browser has not yet received any requests from the server.

For example, the browser only requested index.html, but the server sent index.html, style.css, and example.png all to the browser. In this case, only one round of HTTP communication is required, and the browser gets all the resources and improves performance.

4. Nginx implementation

From the version 1.13.9, Nginx starts to support server push. We have learned to make the Nginx container in the previous tutorial, now let's use it.

First, go to the working directory and delete the original home page.

$ cd nginx-docker-demo
$ rm html/index.html

Then, create a new html/index.html file, and write the source code of the first section of this article inside the file.

We will also need to create two new files which are named example.png and style.css under the subdirectory of html. The example.png file can use any PNG image, while the style.css file should be written some styles inside it.

h1 {
  color: red;

Finally, open the configuration file conf/conf.d/default.conf and change the part of port 443 to the following code.

server {
    listen 443 ssl http2;
    server_name  localhost;

    ssl                      on;
    ssl_certificate          /etc/nginx/certs/example.crt;
    ssl_certificate_key      /etc/nginx/certs/example.key;

    ssl_session_timeout  5m;

    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers   on;

    location / {
      root   /usr/share/nginx/html;
      index  index.html index.htm;
      http2_push /style.css;
      http2_push /example.png;

In fact, it's just added two more lines of the http2_push commands at the end of the code. If the user requests the root path /, then it will push style.css and example.png.

Now start the container.

$ docker container run \
  --rm \
  --name mynginx \
  --volume "$PWD/html":/usr/share/nginx/html \
  --volume "$PWD/conf":/etc/nginx \
  -p \
  -p \
  -d \

Open your browser and visit The browser will prompt that the certificate is not safe, well, ignore it and continue to visit, and you will see the page.

You can't see the server push on the page. You must open the "Developer Tools" and switch to the Network panel. Then you can find that only one request was sent but both style.css and example.png were pushed.

Close the container.

$ docker container stop mynginx

5. Apache implementation

It's similar in the Apache. You can open the server push in the configuration file httpd.conf or .htaccess.

<FilesMatch "\index.html$">
    Header add Link "</styles.css>; rel=preload; as=style"
    Header add Link "</example.png>; rel=preload; as=image"

6. Back-end implementation

The above server push needs to be written in the server configuration file. It's inconvenient obviously. You must restart the service every time you make changes. Morever, the application and server configuration shouldn't be put together.

There is another way to implement server push. The back-end application generates header Link command. Once the server finds the header message, it will push from the server.

Link: </styles.css>; rel=preload; as=style

If you want to push multiple resources, you can write as follows.

Link: </styles.css>; rel=preload; as=style, </example.png>; rel=preload; as=image

See the implementation examples of GoNodePHP.

Now, the configuration of Nginx has been changed to the following.

server {
    listen 443 ssl http2;

    # ...

    root /var/www/html;

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

If the server or browser doesn't support HTTP/2, the browser will preload the specified resource file to handle the header.

In fact, the header message is proposed by the preload standard, and its syntax and the values of as attribute ​​are written in the standard.

7. the cache problem

However, there is a very headache problem in server push. If the browser has already cached the resource files which are to be pushed, pushing is just a waste of bandwidth. Even if the version of the files to be pushed has been updated, the browser will use the local cache as a matter of priority.

One solution is to enable server push only for first-time users. Here is an example given by Nginx official, and it determines whether it is the first visit according to the cookie.

server {
    listen 443 ssl http2 default_server;

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

8. Performance enhancements

Server push can improve performance. As a result of the online assessment, enabling the feature is 8% faster than when it's not enabled, and 5% faster than HTTP/1 of which all resources are embedded in web pages.

It can be seen that the time has not decreased a lot, which is about a few hundred milliseconds. Moreover, it's not recommended to push too many resources at a time, which will impair performance, because the browser has to handle all the resources that are pushed. Pushing only CSS stylesheets may be a good choice.

9. Reference

0 Comment