Backend Development 7 min read

Implementing HTTP/2 Server Push in Go

This article demonstrates how to add on‑demand and periodic HTTP/2 server‑push functionality in Go, explaining the required handlers, showing complete Go code examples, and detailing the HTTP/2 frame exchange observed with an h2c client.

360 Tech Engineering
360 Tech Engineering
360 Tech Engineering
Implementing HTTP/2 Server Push in Go

In the previous article we learned how to send HTTP/2 headers from the server while keeping the connection alive; this article shows how to automatically issue server push.

The author from the 360 Search Technology Team provides a Go implementation that adds two handlers: /crt serving a certificate and /push that triggers a push of /crt to the client.

package main

import (
    "html/template"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/header", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Add("X-custom-header", "custom header")
        w.WriteHeader(http.StatusNoContent)
        if f, ok := w.(http.Flusher); ok {
            f.Flush()
        }
        select {}
    })

    // handler for /crt, returns certificate content for client push
    http.HandleFunc("/crt", func(w http.ResponseWriter, r *http.Request) {
        tpl := template.Must(template.ParseFiles("server.crt"))
        tpl.Execute(w, nil)
    })

    // request this path to trigger push of /crt
    http.HandleFunc("/push", func(w http.ResponseWriter, r *http.Request) {
        pusher, ok := w.(http.Pusher)
        if !ok {
            log.Println("not support server push")
        } else {
            err := pusher.Push("/crt", nil)
            if err != nil {
                log.Printf("Failed for server push: %v", err)
            }
        }
        w.WriteHeader(http.StatusOK)
    })

    log.Println("start listen on 8080...")
    log.Fatal(http.ListenAndServeTLS(":8080", "server.crt", "server.key", nil))
}

Running the server with TLS and using the h2c client demonstrates the exchange of HEADERS, PUSH_PROMISE, and DATA frames, with the client receiving the promised resource without an explicit request.

An automatic push example uses a timer (time.Tick) inside the handler to repeatedly push /crt every 5 seconds, incrementing the promised stream IDs by 2 each time.

http.HandleFunc("/autoPush", func(w http.ResponseWriter, r *http.Request) {
    w.Header().Add("X-custom-header", "custom")
    w.WriteHeader(http.StatusNoContent)
    if f, ok := w.(http.Flusher); ok {
        f.Flush()
    }
    pusher, ok := w.(http.Pusher)
    if ok {
        for {
            select {
            case <-time.Tick(5 * time.Second):
                err := pusher.Push("/crt", nil)
                if err != nil {
                    log.Printf("Failed for server push: %v", err)
                }
            }
        }
    }
})

The article also shows the internal Go HTTP/2 implementation of the Push method, how a PUSH_PROMISE frame is constructed, and how the server processes push requests via the serve loop.

By following these examples, developers can implement both on‑demand and periodic server‑push functionality in Go.

BackendGoNetworkingHTTP/2Server Push
360 Tech Engineering
Written by

360 Tech Engineering

Official tech channel of 360, building the most professional technology aggregation platform for the brand.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.