First attempt at HTTP2 server push

From the public account: New World Grocery Store

In HTTP1.x, when accessing a page, the browser first obtains HTML resources, and then incrementally obtains other resources when parsing the page. The server must wait for the browser to issue a request before delivering the resources in the page. The server actually knows what resources are in the page. If the server can push the resources to the browser before the browser explicitly requests the resources, the page loading speed will be greatly prompted, which is also the theme of this article.

This article is mainly divided into two parts. The first part is an example of server push implemented by go, and the second part is a self-signed certificate. Why is there a self-signed certificate? Here the author sells it first. If you continue to read the article, you will be able to see the moon.

Server push example

Currently only HTTP2 supports server push, HTTP1.x does not support server push, so how should we judge whether the current server supports push in the code?

In Go, we can know whether the current server supports push by judging whether http.ResponseWriter implements the http.Pusher interface.

The following is the first server push example I wrote:

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path != "/" {
        http.NotFound(w, r)
    pusher, ok := w.(http.Pusher)
    if ok {
        // Actively push service resources
        if err := pusher.Push("/static/app.js", nil); err != nil {
            log.Printf("Failed to push: %v", err)
        if err := pusher.Push("/static/style.css", nil); err != nil {
            log.Printf("Failed to push: %v", err)
    // Download browser first screen resources
    fmt.Fprintf(w, `<html>
        <title>Shinsegae grocery store</title>
        <link rel="stylesheet" href="/static/style.css"">
        <div>Hello Shinsegae grocery store</div>
        <div id="content"></div>
        <script src="/static/app.js"></script>
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
http.ListenAndServe(":8080", nil)

The content of app.js in the above code is as follows:

document.getElementById("content").innerHTML = '"Shinsegae grocery store" from js'

After running the above code, visit http://localhost:8080/ in the browser and get the following result:

Looking at the red part in the figure above, we found that the js resources and style resources are not pushed and delivered by the server, and HTTP1.1 is used. Combined with the previous article of the author, we know that Go supports HTTP2 and HTTP2 needs to use SSL/TLS or HTTPS. Therefore, the author modified the monitoring code as follows:

http.ListenAndServeTLS(":8080", "ca.crt", "ca.key", nil)

Run the above code again and visit https://localhost:8080/ in the browser to get the following result:

We can see from the red part in the figure that this request uses the HTTP2 protocol, and the static resources are pushed by the server.

In the above code, ca.crt and ca.key are the self-signed certificate and private key, respectively. The certificate and private key have been uploaded to the author's github, and the github link is at the end of the article.

Generate self-signed certificate

Note: The author generates the certificate environment for macOS

When the author generates a self-signed certificate, he first resorts to the search method, and then uses the online command to generate the certificate. Finally, the certificate is generated, but the access results in the browser after running the above example are not satisfactory.

First execute the following command to generate the certificate:

// Generate private key
openssl genrsa -out old.key 2048
// Generate a certificate signing request, you need to fill in some certificate information at this time
openssl req -new -key old.key -out old.csr
// Issuing a certificate
openssl x509 -req -days 365 -in old.csr -signkey old.key -out old.crt

Modify the certificate and private key in the example to old.crt and old.key, and finally access the results in the Chrome browser as follows:

The above page gives unsafe tips but cannot continue unsafe access (Safari browser can access unsafely). As a patient with mild obsessive-compulsive disorder, I feel a little uncomfortable in my heart.

So the author turned over the openssl official website, and finally made improvements to the command to generate a self-signed certificate.

First create a ca.cnf in the directory where the command is executed (you can name it arbitrarily), and write the following content in the file:

# For more x509v3_config see
[ ca_conf ]
extendedKeyUsage = serverAuth # The key to being able to continue unsecured access
basicConstraints = CA:FALSE

The improved signature command is as follows:

// Generate private key
openssl genrsa -out ca.key 2048
// Generate a certificate signing request, you need to fill in some certificate information at this time
openssl req -new -key ca.key -out ca.csr
// Issuing a certificate
openssl x509 -req -sha256 -extfile ca.cnf -extensions ca_conf -days 365 -in ca.csr -signkey ca.key -out ca.crt

This certificate is the one used in the previous example, and the first time you access it in a browser, the following prompt will appear:

Click the red box in the figure to access the page normally.

When to use push

After completing the above example, the author deliberately observed the homepages of Baidu, Taobao and Google and found that everyone has started to use HTTP2, but it seems that no company has used server push.

Therefore, the following summary of the author is based on personal experience guessing:

  1. Do not abuse the server push, only push the key resources that affect the display of the page. After all, the front-end lazy loading is very mature.
  2. Browsers can cache resources, and it is meaningless to continue pushing resources that have already been cached, so in this scenario, a second push should be avoided.

Finally, I sincerely hope that this article can be helpful to all readers.

refer to


  1. At the time of writing this article, the go version used by the author is: go1.15.2
  2. The complete example used in the article:

Tags: Go

Posted by evilMind on Sat, 07 May 2022 20:59:03 +0300