Frontend Development 10 min read

Understanding HTTP/2 Server Push with Node‑SPDY Examples

This article explains the concept of HTTP/2 server push, demonstrates how to implement it using the node‑spdy module with code samples for pushing assets, redirects, and SSE notifications, and discusses practical considerations and future possibilities for optimizing web performance.

Art of Distributed System Architecture Design
Art of Distributed System Architecture Design
Art of Distributed System Architecture Design
Understanding HTTP/2 Server Push with Node‑SPDY Examples

HTTP/2 allows a server to send multiple responses for a single client request, known as server push, which can eliminate the round‑trip latency caused by waiting for the browser to discover additional resources such as JavaScript, CSS, or images referenced in an HTML page.

In effect, an inline resource that is part of the parent document mimics server push, but HTTP/2 makes this pattern more efficient and powerful because pushed resources can be cached independently and reused across multiple pages.

Getting Started with HTTP/2 Server Push

Inline resources cannot be cached separately and must be duplicated across pages, whereas pushed resources can be cached by the browser and reused. The following minimal SPDY server, built with the node‑spdy module, demonstrates pushing a JavaScript file to the client.

spdy.createServer(options, function(req, res) {
  // push JavaScript asset (/main.js) to the client
  res.push('/main.js', {'content-type': 'application/javascript'}, function(err, stream) {
    stream.end('alert("hello from push stream!")');
  });

  // write main response body and terminate stream
  res.end('Hello World! <script src="/main.js"></script>');
}).listen(443);

When the browser parses the script tag, main.js is already cached, avoiding an extra network round‑trip. This technique replaces inline resources and is supported by browsers that implement SPDY/HTTP‑2 such as Firefox, Opera, and Chrome.

What Else Can We Push?

Beyond JavaScript assets, any HTTP response can be pushed, including redirects. The example below pushes a 301 redirect to a new JavaScript file.

spdy.createServer(options, function(req, res) {
  // push JavaScript asset (/newasset.js) to the client
  res.push('/newasset.js', {'content-type': 'application/javascript'}, function(err, stream) {
    stream.end('alert("hello from (redirected) push stream!")');
  });

  // push 301 redirect: /asset.js -> /newasset.js
  res.push('/asset.js', {':status': 301, 'Location': '/newasset.js'}, function(err, stream) {
    stream.end('301 Redirect');
  });

  // write main response body and terminate stream
  res.end('<script src="/asset.js"></script>');
}).listen(443);

Similar logic can be used to push cache‑invalidations or future‑dated 304 responses, allowing the server to actively manage client caches. Over‑aggressive push must be balanced with client‑side limits to avoid performance penalties.

Client Notification for Server Push

HTTP/2 server push does not provide a JavaScript API for notification, but it can be combined with Server‑Sent Events (SSE) to inform the client when a pushed resource is available. The following server code creates an SSE stream and pushes JSON messages into the cache every two seconds.

spdy.createServer(options, function(req, res) {
  // set content type for SSE stream
  res.setHeader('Content-Type', 'text/event-stream');

  var messageId = 1;
  setInterval(function(){
    var msg = JSON.stringify({msg: messageId});
    var resourcePath = '/resource/' + messageId;
    res.push(resourcePath, {}, function(err, stream) { stream.end(msg); });

    // notify client that resource is available in cache
    res.write('data:' + resourcePath + '\n\n');
    messageId += 1;
  }, 2000);
}).listen(443);

On the client side, an EventSource receives the SSE notifications, then fetches the pushed resource via XHR, demonstrating how pushed data and SSE can work together over a single TCP connection without additional overhead.

<script>
  var source = new EventSource('/');
  source.onmessage = function(e) {
    document.body.innerHTML += "SSE notification: " + e.data + '<br />';
    var xhr = new XMLHttpRequest();
    xhr.open('GET', e.data);
    xhr.onload = function() {
      document.body.innerHTML += "Message: " + this.response + '<br />';
    };
    xhr.send();
  };
</script>

Server Push Innovation

Server push opens many optimization opportunities. Open questions include which resources should be pushed, when to push them, how to detect cache state, whether the server can automatically decide what to push, and how to design applications to maximize the benefits of push.

What resources should be pushed and under what conditions?

Can the server automatically infer push candidates?

Should advanced features like proactive cache management be supported?

How should applications be architected to reap the most benefit?

In the long term, server push may become a killer feature of HTTP/2, enabling browsers and servers to collaborate more intelligently for faster, more efficient web delivery.

Node.jsweb performanceHTTP/2Server PushSSESPDY
Art of Distributed System Architecture Design
Written by

Art of Distributed System Architecture Design

Introductions to large-scale distributed system architectures; insights and knowledge sharing on large-scale internet system architecture; front-end web architecture overviews; practical tips and experiences with PHP, JavaScript, Erlang, C/C++ and other languages in large-scale internet system development.

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.