HTTP/2 did not fix the image

Multiplexing changed M31V’s request economics in 2015, but a large photograph still cost transfer, decoding, memory, and attention.

When HTTP/2 became a published standard in May 2015, M31V had exactly the kind of site people used to explain its promise: many small static resources, repeated headers, and pages assembled from cacheable files.

The protocol allowed concurrent exchanges to share a connection and compressed repetitive headers. That challenged several HTTP/1.x habits, especially combining unrelated files only to reduce requests.

I enabled an HTTP/2 test host and opened an article with a 1.4-megabyte photograph.

The photograph was still 1.4 megabytes.

The protocol improved how resources traveled together. It did not decide whether the page should send those resources, how quickly the phone could decode them, or whether the image was appropriate for its rendered size.

The old budget had two kinds of debt

C62Y's 2013 performance work had treated request count as a major constraint. Under HTTP/1.1, several connections, serialized work within each, repetitive request headers, and connection setup made many small files expensive.

I had responded with familiar techniques:

  • combine scripts and styles,
  • use an icon sprite,
  • place related small images into one sheet,
  • keep the first-view request count low,
  • share a small number of origins.

Some of those decisions also improved compression or reduced overhead. Others existed primarily to work around request concurrency.

HTTP/2 changed the transport tradeoff. Multiple streams could be interleaved over one connection, and header compression reduced repetitive metadata. It did not make bytes, parsing, execution, or image decoding free.

M31V separated its budget into transport overhead and resource consequence. The first category had changed materially. The second had not.

One waterfall no longer told the whole story

In the HTTP/1.1 test, the waterfall showed resources waiting behind one another across a small connection pool. Combining several stylesheets shortened that queue.

Under HTTP/2, requests could overlap on streams inside one connection. The waterfall looked busier and often completed earlier. Reading it with the old rule—fewer bars means faster—would reward bundling even when separate cacheable files behaved better.

I added different questions:

when was the connection established?
which requests shared it?
which response bytes contended for bandwidth?
which resource blocked useful rendering?
what work happened after transfer?
which bytes were already cached on the next page?

The protocol preserved HTTP semantics while changing their mapping to a connection. M31V's HTML, caching headers, content types, and conditional requests still mattered.

A waterfall remained useful evidence. It stopped being reducible to a request-count score.

HTTP/2 excitement made it tempting to split every component into its own file. Multiplexing reduced one cost of that choice, not every cost.

More files still meant more build entries, cache metadata, invalidation decisions, and opportunities to load code a page did not use. Tiny responses still carried framing and header work even when compressed. Browser parsing and execution followed resource contents, not the elegance of the file tree.

I unbundled M31V's article code from its preview-only tools and separated rarely changed base styles from article-specific enhancements. Those boundaries improved caching and ownership.

I did not emit one stylesheet per component automatically. The publication had a small shared design, and a compact base file remained easier to reason about. The split followed change cadence and page need rather than a slogan that requests were now free.

Protocol capability widened the sensible design space. It did not select a file architecture for the project.

Multiplexing allowed other responses to make progress while the large image transferred. The image still occupied bandwidth shared by those streams.

On a constrained connection, sending 1.4 megabytes for a photograph displayed at 640 pixels delayed later resources and spent data a reader might pay for. HTTP/2 framing could interleave bytes; it could not compress an already compressed JPEG into the right crop.

The phone also had to decode the image into memory. Compressed transfer size understated its in-memory footprint. A large source downscaled by CSS still required the browser to receive and decode the large source.

M31V generated several derivatives and selected an appropriate candidate for the layout and display conditions supported by the target browsers. Dimensions in markup reserved space. A larger source remained available when the reading task justified it.

The correct fix was content and image policy, not a protocol upgrade.

Responsive images changed the source decision

By 2015, responsive image support was becoming practical enough to incorporate deliberately, with srcset and related markup allowing the browser to choose among candidates under its own conditions.

M31V's asset graph recorded width, height, format, and approximate byte size for each generated derivative. The renderer emitted candidates suited to the article's layout and kept a conservative fallback.

<img
  src="workbench-640.jpg"
  srcset="workbench-640.jpg 640w, workbench-960.jpg 960w, workbench-1440.jpg 1440w"
  sizes="(min-width: 48rem) 42rem, 100vw"
  width="960"
  height="640"
  alt="A workbench with labeled hand tools"
>

The exact candidates followed real layouts, not every round width. I tested browser choice on reference devices and connections rather than assuming more variants always helped.

HTTP/2 made fetching one selected candidate coexist better with other requests. Responsive markup kept the browser from fetching the wrong candidate in the first place.

HTTP/2 included stream prioritization concepts. Server and browser implementations could use them to improve completion order. I did not turn that into an excuse to mark everything important.

M31V's strongest prioritization remained in document structure. The article heading and text appeared in HTML. Critical styles were small and directly discoverable. The main image occurred where the story needed it. Secondary galleries did not block the opening paragraph.

Resource hints and server behavior were tested as optimizations, not the only route to a usable page. Implementations could differ, intermediaries could affect behavior, and an overenthusiastic server policy could compete with the browser's knowledge.

The build manifest classified resources by role: required for first readable content, visible supporting media, interaction requested later, or preview-only. That editorial classification informed loading decisions across protocols.

Priority worked best after the product had decided what deserved priority.

HTTP/2 server push could send representations before the browser explicitly requested them. The idea was exciting for M31V's predictable article shell.

The difficult questions appeared immediately:

  • Did the browser already have the asset cached?
  • Which article-specific resources were actually necessary?
  • Could speculative bytes compete with the article image or HTML?
  • How would the deployment server map immutable release assets to push rules?
  • What evidence would show that push improved the route rather than the first synthetic load?

I tested a narrow push of a shared stylesheet and found the setup more fragile than the benefit for this small publication. Ordinary discovery and cache headers were already effective. I removed the experiment rather than making a novel protocol feature part of every release.

Capability was not obligation. M31V could benefit from multiplexing and header compression without adopting every HTTP/2 feature.

Domain sharding became a liability

Older performance advice sometimes spread assets across hostnames to gain more HTTP/1.x connections. M31V had a legacy image hostname for that reason.

Under HTTP/2, another hostname could require another DNS lookup, connection, and secure negotiation instead of sharing the existing connection. The sharding workaround could prevent the new transport from combining streams efficiently.

I moved publication assets toward one deliberate origin policy and kept separate origins only when ownership or security required them. Redirect and cache behavior preserved old public asset URLs during migration.

The test compared cold and warm routes. A warm DNS cache could hide the connection cost, while a synthetic single page could miss cross-article cache reuse.

The protocol change did not merely add a faster path. It made some earlier optimizations candidates for removal.

The icon sprite reduced HTTP/1.x requests. It also ensured consistent artwork and one cached asset. HTTP/2 weakened the first reason.

M31V moved simple interface icons toward inline or shared SVG where semantics and styling benefited. Article illustrations remained separate content assets. I did not split one sprite into dozens of tiny files solely to celebrate multiplexing.

Inlining had its own cost: repeated markup in every document and bytes that could not be cached independently. External SVG had accessibility, caching, and cross-document reuse considerations. The right choice followed the icon's role.

This became a useful migration rule: revisit the reason behind an optimization. If request avoidance was its only remaining value, remove it. If the technique still improved ownership, compression, or consistency, keep it for those reasons and measure the tradeoff again.

Historical workarounds often acquire secondary uses. Protocol progress does not automatically make their whole design obsolete.

TLS was part of the practical path

Browser support for HTTP/2 was closely tied in practice to secure connections. M31V's protocol test therefore intersected with its HTTPS migration: certificate deployment, redirects, mixed content, and secure asset origins.

Enabling HTTP/2 on one host while article images remained on an insecure or separately negotiated origin would produce a misleading evaluation. The route needed its real origin and certificate shape.

I kept the changes separable in the release plan. HTTPS correctness came first. HTTP/2 activation had its own verification and rollback. If the protocol configuration failed, the publication could serve correct HTTPS over the previous HTTP version rather than reverse the security migration.

This also prevented a speed improvement from receiving credit for changes actually caused by connection consolidation or cache cleanup during HTTPS work.

Performance experiments need a clear account of which boundary changed.

Compression still belonged to each representation

HTTP/2 compressed headers with HPACK. It did not replace content compression for HTML, CSS, JavaScript, or suitable text formats.

M31V continued to serve compressed textual representations and already-compressed image formats appropriately. It verified content type, content encoding, cache variation, and decompression support across the target path.

I avoided compressing formats that gained little while consuming server work, and I kept precompressed static variants aligned with immutable artifact identity. A deployment could not activate HTML without its matching compressed representation.

The large photograph's JPEG compression was also not evidence it was efficient. Dimensions, crop, quality, and subject determined whether those compressed bytes supported the article.

Protocol-level header compression solved repeated metadata overhead. Representation design remained a content responsibility.

The test route included a second article

A cold single-page benchmark favored choices that front-loaded everything into one response. M31V readers often moved from an archive to an article and then to another article.

I measured a route:

open topic archive cold
open article A
open related article B
return to archive

The route showed cache reuse, conditional requests, shared connection behavior, and whether article-specific assets polluted later work. Splitting a rarely changed base stylesheet from an article enhancement helped here even if one cold waterfall showed an extra request.

Image policy also benefited. Article B did not download gallery assets from A, and cached shared UI remained reusable. The budget recorded entry cost and route cost separately.

HTTP/2 improved concurrency within the route. Cache boundaries decided which work did not need to happen again.

A faster network trace could end while the browser was still decoding the photograph, parsing JavaScript, calculating layout, and painting.

I profiled the reference phone after transfer. One large image caused a memory spike and visible pause during decode. An article enhancement performed document-wide initialization despite using only one figure. Neither problem appeared in the HTTP/2 negotiation.

M31V retained budgets for decoded image dimensions, script execution, DOM size, and interaction behavior. Transfer and request measurements joined them rather than replacing them.

The project also watched layout stability. Explicit image dimensions prevented content from moving after delayed media arrived. A protocol can deliver bytes concurrently and cannot make an unknown aspect ratio reserve the right space.

Performance remained end-to-end interface behavior. The network protocol owned only part of the path.

Failure moved to one shared connection

Multiplexing several streams over one connection reduced the need for many connections and made that connection more consequential. Packet loss at the TCP layer could still affect progress for streams sharing it.

I did not attempt to outsmart transport behavior from application code. I tested under loss and variable latency, kept critical content small, and avoided loading unnecessary media that increased contention.

The result was a more nuanced expectation than “no head-of-line blocking.” HTTP/2 removed HTTP/1.x response serialization within a connection through independent streams. Its streams still traveled over TCP in the 2015 protocol stack.

That distinction mattered when results on a clean link did not repeat on a lossy mobile connection.

The publication benefited without pretending the protocol had abolished network physics.

The server configuration became release evidence

Static artifacts alone did not prove HTTP/2 was active. Host configuration, certificate path, protocol negotiation, caching, and fallback behavior belonged to deployment verification.

The smoke check recorded:

secure origin and certificate validity
negotiated protocol on representative clients
correct content encoding and type
cache headers for fingerprinted assets and documents
canonical redirects
HTTP/1.1 fallback behavior

The build manifest still described resource sizes and relationships. A deployment manifest described how the host served them.

I kept fallback because not every client negotiated HTTP/2 immediately. The publication's semantics and readable HTML did not depend on the newer transport.

Adopting a protocol meant verifying the actual network path, not adding a line to a server file and assuming every request used it.

Browser support changed rollout, not source semantics

The 2015 client landscape required a measured rollout. M31V tested representative current browsers that negotiated HTTP/2 and retained correct HTTP/1.1 behavior for the rest. The same canonical URLs and representations worked over either protocol.

I did not create an h2 version of the article or send lower-quality images merely because a connection used HTTP/1.1. Resource selection followed viewport, display, content role, and browser capability. Transport negotiation remained beneath the URL.

This made comparison safer. A request could fall back without changing publication meaning. Logs recorded negotiated protocol so performance samples were not mixed unknowingly.

The rollout began on a test hostname, moved to a small public path, then reached the full static origin after certificate, cache, and fallback checks. Reversal disabled the new protocol while preserving HTTPS and the artifact.

Progressive deployment kept protocol adoption from becoming a synchronized rewrite of content, build, and server. The publication could learn from real connections without making newer transport a prerequisite for reading.

The budget changed instead of disappearing

M31V relaxed its crude initial-request ceiling. A small, independently cacheable resource could now justify itself without being bundled solely to avoid another serialized HTTP/1.x request.

The revised budget emphasized:

  • bytes needed before the article became useful,
  • total bytes by resource role,
  • selected image candidate versus rendered need,
  • main-thread and decode work,
  • cache reuse over a reading route,
  • origin and connection count,
  • request count as evidence, not a verdict.

Violations still required a tradeoff. HTTP/2 expanded which tradeoffs could be good.

The large image failed under both protocols because it spent too much transfer and memory for its role. A separate small stylesheet could pass under HTTP/2 because it improved caching and did not delay readable content materially.

Budgets should evolve when system costs evolve. They should not vanish when one cost becomes cheaper.

The protocol fixed the problem it named

HTTP/2 was a substantial improvement. It allowed concurrent exchanges on one connection, compressed repetitive headers, and made old request-avoidance habits worth reconsidering. M31V's multi-resource pages used the network more effectively after careful deployment.

It did not choose image dimensions, remove unused JavaScript, define critical content, reserve layout space, secure old asset URLs, or decide which resource deserved the opening moment.

The 1.4-megabyte photograph became a useful test because it refused to be optimized by enthusiasm. The right derivative reduced its transfer under HTTP/1.1 and HTTP/2. The right markup let the browser choose. The right placement kept it from blocking the article's first idea.

New infrastructure often removes a constraint that shaped old architecture. The responsible response is to revisit those decisions, not to declare constraints over.

HTTP/2 fixed important transport problems. The image was still ours.