Performance & Optimization

HTTP/2 Server Push: Promise and Pitfalls

How HTTP/2 server push works, why it was largely abandoned, and what replaced it — 103 Early Hints, preload headers, and speculation rules.

How HTTP/2 Server Push Works

HTTP/2 Server Push was designed to solve the waterfall problem: the browser requests an HTML page, but it cannot know which CSS, JS, and fonts to fetch until it parses the HTML. Server Push lets the server proactively send those resources before the browser even asks.

The protocol mechanism is the PUSH_PROMISE frame:

Client → GET /index.html  (HTTP/2 stream 1)

Server → PUSH_PROMISE     (announces /style.css will be pushed)
       → PUSH_PROMISE     (announces /app.js will be pushed)
       → DATA /index.html (stream 1 response body)
       → DATA /style.css  (stream 2: pushed CSS)
       → DATA /app.js     (stream 3: pushed JS)

The PUSH_PROMISE frame contains the full request headers (including the URL) for the resource being pushed. The browser can then match this against its cache — if it already has the resource, it can send RST_STREAM to cancel the push.

Server Push in Practice (Nginx)

server {
    listen 443 ssl http2;

    location = /index.html {
        http2_push /style.css;
        http2_push /app.js;
        http2_push /fonts/inter.woff2;
    }
}

Or via Link response headers:

HTTP/2 200 OK
Link: </style.css>; rel=preload; as=style
Link: </app.js>; rel=preload; as=script

Why Server Push Failed

Despite the elegant concept, HTTP/2 Server Push had fundamental problems that led Chrome to remove support in Chrome 106 (October 2022):

The Cache Problem

The biggest issue: the server cannot know what the browser has cached. If a returning visitor has /style.css cached, the server still pushes it — wasting bandwidth. The browser can cancel the push via RST_STREAM, but the server has often already started sending data by the time the cancellation arrives.

Studies found that aggressive server push consistently increased page load times for returning visitors due to wasted bandwidth crowding out actual content.

Priority and Flow Control

HTTP/2's stream priority was poorly implemented by many servers and browsers. Pushed resources often competed with the main document and won — delaying the HTML parse that the browser needed to make further decisions.

Certificate/Authentication Complexity

Pushed resources must be on the same origin (same certificate). You cannot push resources from a CDN or third-party origin, which limits real-world applicability.

The Replacement: 103 Early Hints

103 Early Hints is a 1xx informational status code that the server sends before the final response is ready. It contains Link headers telling the browser which resources to preconnect to or preload:

HTTP/1.1 103 Early Hints
Link: </style.css>; rel=preload; as=style
Link: </app.js>; rel=preload; as=script
Link: <https://fonts.googleapis.com>; rel=preconnect

HTTP/1.1 200 OK
Content-Type: text/html
...

The key advantage over Server Push: 103 Early Hints tells the browser to fetch resources using its own cache-aware logic. The browser checks its cache first, fetches only what it does not have, and manages priorities correctly.

# Nginx 1.25+: send 103 Early Hints
location = / {
    early_hints on;
    early_hint_types text/html;
    add_header Link "</style.css>; rel=preload; as=style" always;
}

Cloudflare automatically converts Link: rel=preload headers from your origin into 103 Early Hints responses — no server configuration needed.

Speculation Rules API

For next-page prefetching (what Server Push was never suited for), the Speculation Rules API provides browser-level prerendering:

<script type="speculationrules">
{
  "prerender": [{
    "urls": ["/checkout", "/dashboard"]
  }],
  "prefetch": [{
    "where": { "href_matches": "/articles/*" },
    "eagerness": "moderate"
  }]
}
</script>

Prerender goes further than prefetch — it fully renders the page in a hidden tab. When the user navigates, the switch is nearly instant. This is the spiritual successor to Server Push's original goal.

Lessons Learned

HTTP/2 Server Push illustrates a recurring pattern in protocol design: a feature that is theoretically optimal in isolated benchmarks but fails due to real-world cache state, network variability, and implementation quality.

The replacement features (103 Early Hints, Speculation Rules) succeed because they move decision-making to the browser, which has the necessary context (local cache contents, user navigation intent, device capabilities) that the server fundamentally lacks.

Key Takeaways

  • HTTP/2 Server Push was removed from Chrome 106 — do not add it to new projects
  • 103 Early Hints is the modern replacement — browser handles cache-aware fetching
  • Cloudflare converts Link: rel=preload headers to 103 Early Hints automatically
  • Speculation Rules API enables prerender for near-instant navigation
  • When a protocol feature requires server knowledge of browser cache state, the design is flawed

Protokol Terkait

Istilah Glosarium Terkait

Lebih lanjut di Performance & Optimization