7 ms·
A Guide to HTTP/2 Server Push
- mozumder 10y ago> As of now, Nginx doesn’t support HTTP/2 server push, and nothing so far in the software’s changelog has indicated that support for it has been added. This may change as Nginx’s HTTP/2 implementation matures. Forget Apache or Nginx. Use the H20 server instead: https://h2o.examp1e.net https://h2o.examp1e.net I've been using it on a production server with a Django app for about a year now, with HTTP/2 push support, and it's been great. It includes advanced feature that knows if a web browser already has pushed content in its cache, so it doesn't resend it. It's architecture seems to offer the best of Nginx's multiprocessing capabilities as well. Configuration is as simple as Nginx as well. (I went from Apache -> Nginx -> H2O)
- vfaronov 10y agoH2O also allows an HTTP/1.1 backend to push by sending links [1] with rel=preload — these links are automatically converted to pushes by H2O. This can be further sped up with early hints [2]. [1] https://tools.ietf.org/html/rfc5988 https://tools.ietf.org/html/rfc5988 [2] https://tools.ietf.org/html/draft-ietf-httpbis-early-hints https://tools.ietf.org/html/draft-ietf-httpbis-early-hints
- alex_duf 10y agoSo wait, if the server pushes assets before the client request it, does that mean the server pushes them regardless of the cache status on the client? If so that should really be limited to small assets to be beneficial
- Clownshoesms 10y agoI think it still obeys the browser's cache: "When the user navigates to a subsequent page that requires that asset, it can be pulled from the cache, eliminating the need for additional requests to the server." I'm not clear on if there is any relation to existing cache headers. I would have thought even the first page hit could have Link'ed assets pulled from the cache if present (if the client had previously visited that push-enabled resource (edit: in which case it's not the first I guess zzz). I guess the client, upon hitting a cached asset referenced in a Link header ignores it where otherwise it'd suck it down the socket as part of the same initial request.
- mozumder 10y agoThe H2O server, on the first request, sends a cookie that includes a signature of all the files a server pushes: https://h2o.examp1e.net/configure/http2_directives.html#http2-casper https://h2o.examp1e.net/configure/http2_directives.html#http... If, on subsequent requests, the cookie is sent along with the request, H2O doesn't send those files. This is still a bit of a hack, and ultimately browsers will have to include some information on what's in the actual cache, with proposals on that pending: https://datatracker.ietf.org/doc/draft-ietf-httpbis-cache-digest/?include_text=1 https://datatracker.ietf.org/doc/draft-ietf-httpbis-cache-di...
- Ajedi32 10y agoAs I understand it, the client has an opportunity to proactively cancel pushes of assets it already has cached. The server will send a "push promise" which basically says "I'm going to send this file to you", and then the client can come back and say "don't bother, I already have it". And this all happens in parallel with the download of other assets (like the main page), so it doesn't really slow anything down. Here's an article I read which goes into how server push works in a lot more detail: https://hpbn.co/http2/#server-push https://hpbn.co/http2/#server-push
- Clownshoesms 10y agoI had a bit of a look around after reading the article, this page might also be useful in explaining how it interacts with the cache and some suggestions on when to apply it: https://www.shimmercat.com/en/blog/articles/whats-push/#interaction-with-the-browser-s-cache https://www.shimmercat.com/en/blog/articles/whats-push/#inte... Sounds painful to me.
- mozumder 10y agoTo set it up isn't that hard... just set it and forget it, and let the browser & server handle things from there. In practice, it's going to be a lot faster than the default of not pushing resources.
- jimktrains2 10y agoDoesn't that require more round trips than just having the client ask for it?
- jaffathecake 10y agoYes. This makes push better for things that you'd otherwise inline. link[rel=preload] is generally good enough for other resources.
- Ajedi32 10y agoNo, even in the worst case it's exactly the same number of round trips. Without server push: 1. Client requests main page -> 2. Client receives main page <- 3. Client requests subresources -> 4. Client receives subresources <- With server push: 1. Client requests main page -> 2. Client receives push promises and main page <- 3. Client cancels promises it doesn't need -> 4. Client receives subresources <- The only difference is that with server push, steps 2, 3 and 4 happen in parallel, and step 3 can be omitted entirely in the event that the client doesn't need to cancel any pushes.
- dullgiulio 10y agoBasically yes. The client can cancel the streams for pushed assets that are in cache. But the server doesn't have this information (natively) when one page is requested. As others have said, there are workarounds using cookies. Which is yet another abuse of a very bad feature of HTTP.
- youngtaff 10y agoI'm not sure that any of the current browsers actually do this though
- EGreg 10y agoPush shouldn't be used for GETting things that you can cache. For that you can use pull with HTTP pipelining. Push should only be used for updates, including updating the cache. Ideally you'd only be sending the deltas (changes).
- jimktrains2 10y agoIt's unspecified how the server should handle this. AFAIK, it's "one of the great unsolved problems" with HTTP2.
- vfaronov 10y agoThere is ongoing work at the HTTP working group to standardize “cache digests” to solve this problem: https://tools.ietf.org/html/draft-ietf-httpbis-cache-digest https://tools.ietf.org/html/draft-ietf-httpbis-cache-digest
- dfabulich 10y agoYou are precisely correct; server push is not cache aware today. According to the spec, browsers have the ability to abort pushes that the server has started, so if you start sending down a 3MB file, your browser can send the server a request to stop sending it, because it's already in cache, but no browser actually does this today, and the first part of the file gets sent down regardless. There's a draft spec for browsers to use "Cache Digests," a compact representation of the content already in the cache. http://httpwg.org/http-extensions/cache-digest.html http://httpwg.org/http-extensions/cache-digest.html But no browsers support that today. Luckily, you can workaround/polyfill Cache Digests using Service Workers. Your Service Worker can provide the Cache Digest as a custom HTTP header on subsequent requests, and then the server can know to avoid re-pushing cached resources. Alternately, and more simply, if you configure your Service Worker to return/render stale content on first load, your Service Worker can make a request in the background to fetch the latest content, passing a simple header to the server to disable push. Since the stale content has already rendered, the round-trip time to download updated resources is less important. http://calendar.perfplanet.com/2016/cache-digests-http2-server-push/ http://calendar.perfplanet.com/2016/cache-digests-http2-serv...
- jbergstroem 10y agoSome web servers[1] additionally stores cache information in cookies. The same author that (co-)wrote your linked draft implemented it as a primer to the draft. If only CDN's supported it :-) [1]: https://h2o.examp1e.net/configure/http2_directives.html#http2-casper https://h2o.examp1e.net/configure/http2_directives.html#http...
- forgotpwtomain 10y ago> but no browser actually does this today, and the first part of the file gets sent down regardless. Are browsers really not doing this? My understanding was that just one round-trip was wasted for the client to send an RST_STREAM frame, or does it really re-download the entire pushed file where normally it would be a 304 on request?
- dfabulich 10y ago
- TYPE_FASTER 10y agoFlashback to the mid-nineties and push media. http://www.javaworld.com/article/2077287/marimba-software--pushes--information-over-internet.html http://www.javaworld.com/article/2077287/marimba-software--p...
- jgrahamc 10y agoLiterally, nothing like that.
- wolfhumble 10y agoAlways remembered the brand and name: Marimba Castanet, but never its purpose – thanks for the refresh! :-)
- lobster_johnson 10y agoI was surprised when I read that Server Push wasn't intended for event sourcing. Anyone know why? These days people use Server Sent Events and WebSockets for this: The client requests data from the server (possibly through a "subscription" type of request), and the server keeps sending messages (e.g. chat messages) until the client cancels the request or closes the connection. Not having looked deeply into Server Push, it sounds ideal for this sort of thing, and then we could do away with both SSE and WebSockets, neither of which are very HTTP-y.
- EGreg 10y agoWhy would you do away with them just because they aren't HTTP-y?
- HorizonXP 10y agoConfusing name but different use cases. HTTP/2 does introduce efficiency gains that will benefit SSE, but it's unlikely to supplant WebSockets entirely. https://www.infoq.com/articles/websocket-and-http2-coexist https://www.infoq.com/articles/websocket-and-http2-coexist
- jaffathecake 10y agoSSE is just a long-running connection, so it fits with HTTP/2 pretty well (and HTTP/1 for that matter). H2 push needs to be linked to an initial response, and I don't think it's acceptable to push resources once the initial response is complete. Also, H2 push is pretty much a request/response pair, so it can be cached, so it doesn't really fit the SSE model. You're right about websockets though. At some point they'll be replaced with something more H2-like, but it won't be push-based.
- dullgiulio 10y agoIf the next protocol iteration is something like QUIC, then the WS could just be implemented over QUIC UDP packets.
- derefr 10y agoRight; Server Push isn't "send the client arbitrary data"; Server Push is "move link/subresource prefetch logic to the server, where you can take advantage of optimizations like single JOIN-queries that give you all you need to output multiple resources, actually outputting multiple responses."
- jakelarkin 10y agoCouple gripes with HTTP2 ... 1) HTTP2 Server Push is a hobbled protocol. It was designed for serving static assets to desktop browsers for pre-caching. Most of the internet traffic now is moving to mobile apps or rich browser clients. Developers would greatly benefit from a true bi-directional API that could be fully leveraged by event-based or reactive frameworks. The community could even build it's own APIs if the browsers just exposed the DataFrame primitives of HTTP2. But they don't. 2) Open-source proxy servers like NGINX, HAProxy and many key IaaS providers like Cloudflare, Google Cloud do not support full duplex HTTP2 connections (one-side only at best, translated to HTTP1 on the backend). This is 1 year after RFC 7540 was officially released and 5 years after SPDY. Why even bother baking considerations for HTTP2 into your application layer when your edge infrastructure won't let you leverage it anyway.
- manigandham 10y agoThe lack of HTTP/2 on the backend to origin connections is a big sad mystery. If anything it would greatly increase performance, especially for smaller servers and longer distances, with the multiplexing and stream priority settings alone. Required TLS would also increase security.
- voidlogic 10y ago>The lack of HTTP/2 on the backend to origin connections is a big sad mystery. Sad, sure, but its not a mystery. As of HTTP/1.1 these proxies already pools TCP/IP connections between the proxy and its origin (the backend server), so it doesn't gain the same connection multiplexing/windowing/cardinality benefits that HTTP/2 to end client gets. This means for many proxy code bases H2 to origin is just a nice to have. Obviously, by prioritizing like that you miss other nice to haves like push.
- manigandham 10y agoHTTP/1 connections are limited to one request on the connection at a time and head-of-line blocking. HTTP/2 connections will also be pooled but are able to sustain many more concurrent requests because of multiplexing, while also setting certain requests as higher priority. It's a major improvement for high volume services. The performance and features are why Google's gRPC uses it as the foundation for microservice communications at scale and low-latency.
- niftich 10y agoIt's true that the mechanics of triggering HTTP/2 Push are done with the Link rel=preload directive (defined by the W3C [1]), the advice to hardcode HTTP headers on outgoing responses that point to file references is simplistic at best -- which shows that this whole thing (still) isn't yet thought out too well. Most of the content of my earlier post on this topic [2] still stands; since then, Caddy has implemented rule-based ways of specifying what to push [3][4] (although not via Google's push manifest [5], despite being asked to). It'd be far more productive if the community coalesced around one clear declarative way of specifying what to push -- Google's push manifest does this well enough -- which could be ingested by server software and applied as needed. If this were done, the problem space is moved to somehow generating that manifest, which can be done by hand, or scraping resources, or as an output of a more complex tool [6] that knows more about the info-space of the resources. And of course, keep in mind the advice that Google has prepared as part of the research they've done on deployments of HTTP/2 Push [7]. [1] https://www.w3.org/TR/preload/#server-push-http-2 https://www.w3.org/TR/preload/#server-push-http-2 [2] https://news.ycombinator.com/item?id=12722383 https://news.ycombinator.com/item?id=12722383 [3] https://github.com/mholt/caddy/issues/816 https://github.com/mholt/caddy/issues/816 [4] https://github.com/mholt/caddy/pull/1215 https://github.com/mholt/caddy/pull/1215 [5] https://github.com/GoogleChrome/http2-push-manifest https://github.com/GoogleChrome/http2-push-manifest [6] https://github.com/webpack/webpack/issues/1223#issuecomment-232311846 https://github.com/webpack/webpack/issues/1223#issuecomment-... [7] https://docs.google.com/document/d/1K0NykTXBbbbTlv60t5MyJvXjqKGsCVNYHyLEXIxYMv0 https://docs.google.com/document/d/1K0NykTXBbbbTlv60t5MyJvXj...
- username223 10y agoLooking at the graphs, all of the variations come in at 2-4 seconds, in most cases 10-25% average difference. So it's all slow, but not unbearable; it seems like a bit of a wash.
- cflat 10y agoUsing `Link: rel=preload` is interesting, but it misses the real opportunity to get the critical resources before the html and window to grow the congestion window early. At best you are in a race condition on the browser's preloader/speculative parser. At worse, you are competing with the html base request with your pushed resources. The better solution is to push these resources before the HTTP headers and status code from the application framework. The real opportunity is to get the content downloaded early, grow the congestion window ahead of receiving the html, then yielding the socket to the html, and then continue pushing content until the browser starts making it's own http requests. As all things - mileage will vary. Some times this is a large opportunity (eg: User in Australia requesting content from NY; or DR failover). Other times, push opportunity might be negligible (eg: cached html page) On high RTT networks, this does save you the full round trip for the request. For more details on this check out my talk at Velocity Amsterdam: https://youtu.be/GjWD1pOkxUk?t=1534 https://youtu.be/GjWD1pOkxUk?t=1534 https://speakerdeck.com/colinbendell/promise-of-push https://speakerdeck.com/colinbendell/promise-of-push I also built a tool (on top of webpagetest.org) that helps you evaluate the potential for push here: https://shouldipush.com https://shouldipush.com
- rb808 10y agoWhat I'd love in the HTTP protocol is some way to control IPs used by client - eg fail over to different IP if first is unresponsive. Such a small client side change would make back ends much simpler.
- jeroenhd 10y agoThere's already a system to do so: it's called DNS. You can check for timeouts using XHR already and if a server doesn't respond then having it send a new IP wouldn't work anyway. The problem with using DNS that way is the usual 30 second time out. On the other hand lower values than 30 seconds can cause problems for many users with slow or mobile connections. Honestly I can't think of a reason why the HTTP specifications should contain anything about IP addresses.