
HTTP 103 – An HTTP Status Code for Indicating Hints - snomad
https://datatracker.ietf.org/doc/draft-ietf-httpbis-early-hints/
======
richdougherty
Summary: a new status code that lets the server send headers early, before the
main headers. This helps with optimisations like preloading. Example from the
document:

    
    
         HTTP/1.1 103 Early Hints
         Link: </main.css>; rel=preload; as=style
    
         HTTP/1.1 103 Early Hints
         Link: </style.css>; rel=preload; as=style
         Link: </script.js>; rel=preload; as=script
    
         HTTP/1.1 200 OK
         Date: Fri, 26 May 2017 10:02:11 GMT
         Content-Length: 1234
         Content-Type: text/html; charset=utf-8
         Link: </main.css>; rel=preload; as=style
         Link: </newstyle.css>; rel=preload; as=style
         Link: </script.js>; rel=preload; as=script
    

The client can start preloading the CSS and JavaScript before the main headers
arrive. This is a nice optimisation.

There are various security risks with sending multiple headers to non-
conforming clients hence: "Therefore, a server might refrain from sending
Early Hints over HTTP/1.1 unless the client is known to handle informational
responses correctly."

~~~
tbodt
Sounds a lot like HTTP/2 push.

~~~
scott_karana
From the RFC intro:

> HTTP/2 ([RFC7540]) server push can accelerate the delivery of resources, but
> only resources for which the server is authoritative. The other limitation
> of server push is that the response will be transmitted regardless of
> whether the client has the response cached. At the cost of spending one
> extra round-trip compared to server push in the worst case, delivering Link
> header fields in a timely fashion is more flexible and might consume less
> bandwidth.

~~~
mozumder
There are cache-aware HTTP2 server push technologies that can be used to cut
that down wasted bandwidth.

See:
[https://h2o.examp1e.net/configure/http2_directives.html#http...](https://h2o.examp1e.net/configure/http2_directives.html#http2-casper)

------
mbell
To explain the use case for this (as I understand it):

http/2 push sounds great at first glance for doing things like sending your
CSS/JS bundles downstream with the HTML. The problem with this is that only
the system that builds the HTML knows what needs to be pushed and only the
system terminating the http/2 connection can send the assets. That means your
app servers would need to terminate the http/2 connection and would be serving
static assets. Serving static assets from your app server sucks for a lot of
reasons. Instead what you want to do is tell some upstream proxy that is good
at serving static assets what it should push. This is done by having the
upstream proxy parse the Link HTTP headers in the response, which many
services already do. But, today they can't start pushing assets until the main
response is being sent since they need the headers from it to instruct them.
Ideally you'd start sending assets ASAP, while the app server is building the
page. This is exactly what the 103 response is most useful for.

e.g. You have a CDN front for your app servers, the app server sends an early
103 as soon as it knows what assets the page needs, the CDN intercepts this
response and uses it to start pushing the assets to the client from the local
edge node using http/2 push. This lets you benefit from http/2 push of your
assets while processing the main response without your app server needing to
serve static assets or even know about http/2 itself.

~~~
pcl
Does HTTP2 support multiple concurrent streams over a single client
connection? If not, presumably your use case would require that the client has
a separate HTTP2 connection opened up to the CDN already, right? Or am I
missing something?

~~~
kevin_nisbet
While I disagree with mbell's interpretation of the value of this header or
how it could be used, to answer you're question pcl, yes HTTP 2 does support
multiple streams over a single transport connection.

To me, I would say increasing efficiency (or decreasing latency) was a central
design goal of HTTP2. So HTTP2 includes it's own framing, which allows
multiplexing across the connection. This helps avoid the overhead of
establishing multiple parallel connections to speed up page load, head of line
blocking, running slow start on each connection, etc.

~~~
mbell
> While I disagree with mbell's interpretation of the value of this header or
> how it could be used

FWIW the use case I'm describing has already been added to Rails / Puma:

[http://eileencodes.com/posts/http2-early-
hints/](http://eileencodes.com/posts/http2-early-hints/)

[https://github.com/rails/rails/pull/30744](https://github.com/rails/rails/pull/30744)

[https://github.com/puma/puma/pull/1403](https://github.com/puma/puma/pull/1403)

~~~
kevin_nisbet
Sorry, I apologize for being unclear, I don't agree with you're interpretation
of the use case that adds the most value.

While the RFC does state that what you described can happen at a caching
intermediary, I don't believe that's where the value lies or the primary use
case. To me, much of the value added is to get the 103 to the client early,
and allow it to begin making decisions on whether the content is in it's local
cache, or to begin making requests to third party servers.

This helps eliminate some of the weaknesses with HTTP push, mainly being that
only content the server is authoritative for can be pushed, and that the
server doesn't know whether the resource to be pushed already exists in a
client cache.

It's a balancing act of course, but if the resource being pushed is above a
certain size, pushing the resource on each request could add overhead, not
reduce it.

~~~
mbell
The problem is that clients won't be supporting 103 for a long time, I would
guess it'll be 3-5 years before you can realistically push the 103 to a
browser and I don't thank any currently support it. It's also not a
particularly safe thing to turn on. e.g. Chrome prior to 57 bombed on http/2
connections that sent a 103 which is in contrast to the RFP's theory that
http/2 clients should be 'safer' in handling it than http/1 clients. We really
don't know how many clients and proxies will have trouble with 103 and have to
wait for the vast majority to at least ignore it / pass it along before it
could be turned on externally.

> This helps eliminate some of the weaknesses with HTTP push, mainly being
> that only content the server is authoritative for can be pushed, and that
> the server doesn't know whether the resource to be pushed already exists in
> a client cache.

When a server sends a push to the client it first sends `PUSH_PROMISE` command
to the client and the client may cancel the push, e.g. if it already has the
asset in cache based on url. Assuming things move forward the server sends a
HEADERS frame which can also trigger to the client to cancel the push, e.g. in
response to an etag, though at this point some DATA frames may be in flight.

You are correct that you can't push assets for another domain, but I don't see
this as much of a problem as there are many other benefits to having all
requests go a CDN/reverse proxy that can handle the static asset serving, like
regional SSL termination. There are of course third party scripts that may be
needed, but it's good practice make these non-blocking for page rendering
anyway so if they are a bit behind that is probably ok, if not preferable.

------
tenderlove
The author of this draft is also the author of H2O (an HTTP/2 webserver). IMO
the cool thing about this RFC is that you can put an HTTP/2 proxy that
understands Early Hints in front of an HTTP/1 application server, and give the
application server HTTP/2 push without needing to "HTTP/2 enable" the
application server.

We added early hints support to Puma:

[https://github.com/puma/puma/pull/1403](https://github.com/puma/puma/pull/1403)

Then added early hints to Rails:

[https://github.com/rails/rails/pull/30744](https://github.com/rails/rails/pull/30744)

Such that if you run H2O (or any webserver that supports early hints) in front
of Rails, you can get asset push support for free. eileencodes wrote a great
blog post about her work on this:

[http://eileencodes.com/posts/http2-early-
hints/](http://eileencodes.com/posts/http2-early-hints/)

------
edoceo
Waiting for [https://http.cat/103](https://http.cat/103) to be updated

------
breakingcups
This is one of those optimizations where I'm ambivalent about it's usefulness.

Sure, Google might get a millisecond or two less in their apps, but who in
their right mind has the engineering time to implement this very, very small
optimization when there's many other things to be done that probably have a
higher priority?

~~~
dsign
You are right, most people don't have the engineering time to implement this.
Though there is definitely a market for this kind of optimizations in a SaaS
solution (I know that because it has something to do with my job)...

Now, implementation of this by browsers is going to take a long time, and then
we will see incompatibilities trickle down all the way, with Safari taking a
few years after everybody else has it and IE/Edge also taking forever.... I
should rather work in a farm and harvest tomatoes...

~~~
CSMastermind
I feel like frameworks will implement it and most devs will get it that way.

~~~
dsign
Yes, most frameworks will implement it. I'm quite sure.

------
buro9
What happens to a Set-Cookie on a 103?

What happens to hints in a redirect chain?, i.e. 301 103 301 103 200

What headers are applicable to the 103 beyond Link? If a 103 included Refresh,
could this be used like a timeout (redirect after Refresh time if actual
request did not respond?)

This...

> A server MAY use a 103 (Early Hints) response to indicate only some of the
> header fields that are expected to be found in the final response.

... implies that all possible HTTP headers from the final response may be in
the 103.

RFCs need less ambiguity, lest clients and servers start to fill in the gaps
and have divergent behaviour.

~~~
sp332
If the implementation decided to honor Set-Cookie in a 301, it should use the
same security boundaries that it would use anywhere else. For a redirect chain
and the rest, again it's up to the implementation. Since this is just an
optional optimization, does it matter if there are divergent clients and
servers?

------
unmole
It isn't yet _approved_. It is still a draft, not an RFC. Even if it does
become an RFC, it is intended for the Experimental track, not the Standards
track.

~~~
TimTheTinker
The HN post title really should be changed. Folks skimming headlines will see
what is, in effect, fake news.

------
kardos
Am I wrong in thinking that these hints should include subresource integrity
hashes?

Given hints with SRI hashes, the client can fetch/verify them and be ready
when the HTML arrives, or even better, consult the cache and verify to avoid
fetching and save bandwidth.

~~~
kevin_nisbet
So I think there are a couple aspects to consider on this.

This RFC very strongly points to hints as the purpose, but to me appears to be
generically implemented. So HTTP 103 can send any header, but only the final
headers are authoritative. So it's really up to wherever those headers are
defined to indicate what can/cannot be indicated by the headers. In other
words, what a header can represent is likely a different discussion.

Subresource integrity is actually a w3c recommendation and not an IETF
recommendation, so the checksum doesn't appear to be conveyed through headers.
But trying to think about it logically, I don't think it's necessary to have
the hashes at this stage. The web browser get's a HTTP 103, and see's the link
rel and begins requesting and caching the resource. The web browser then loads
the HTML, which includes the subresource integrity, that it then uses to check
the result it already downloaded and cached. To validate that resource, it
would need to download it anyway, the only thing HTTP 103 did was allow the
the content to start downloading sooner in the process.

~~~
Too
You don't need to download the file to verify the contents. The purpose of
having a hash in the link is to allow you to cache jQuery previously
downloaded from one CDN even though the link points to another.

------
cbsmith
At some point we're just going to go with a full-on bidirectional stateful
protocol, right? ;-)

~~~
tzahola
And we could call it HTTCP

~~~
JetSpiegel
Or for branding, Websockets

------
pebers
Are there any prior examples of sending multiple status codes in one response?
I didn't think you could, but there's not much discussion of it in the draft
(a little in section 3 only) which makes me think it's not seen as
controversial. Seems like it'd be a heap of work to update all the servers and
clients out there if it's not supported already?

~~~
aexaey
Yes. "100 Continue". [1]

If client intends to send a POST request with request body large enough to try
to avoid wasting bandwidth (in case server might reply with 3xx,4xx or 5xx),
the client will send _only_ _headers_ including "Expect: 100-continue" header,
then server indenting to reply with "200 OK", will instead first reply with
"100 Continue" than client will send POST body, and finally server sends "200
OK".

[1]
[https://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8....](https://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.2.3)

------
badsectoracula
This look nice to prefetch some stuff, although i have a feeling it'll break
some not-so-nicely coded HTTP 1.1 clients that expect everything after the
first CR/LF to be the content or -more likely- if the response isn't 200 for
the request to fail :-P.

------
Animats
Can this be abused by middleboxes to stick in an interstitial ad before and
during page load?

~~~
DiabloD3
No, its pretty much only useful for hinting css and js files that will be
loaded in the <head>.

~~~
rhizome
so...JS _injection_ then?

~~~
traek
No. The JS would still be need to referenced by the page, this just affects
preloading of assets.

~~~
rhizome
So a far-side check, but does it not execute it until everything checks out?
Y'know, now that I think about it more.

~~~
vertex-four
It literally just says "please download this into the browser cache". Nothing
happens to things in the browser cache - they certainly don't get executed.
Later, if a page tries to load resources, it might come from the browser
cache.

~~~
est
> Nothing happens to things in the browser cache

Really? Let's see, at least one extra DNS lookup can happen, a URL fetch can
happen, this is totally enough for advertising tracking.

~~~
colanderman
If you are in a position to inject a 103 response into a stream, you are in a
position to inject JavaScript directly into the HTML body.

~~~
est
I didn't say it's an inject, it will be abused and purposely setup as a
tracking tool by the webpage providers.

It's a sneaky unwanted network request un-controllable by end users. And it
happens even before you load the page.

All ad-blocking tools/addons today works by parsing the webpage content, the
103 header extended the game from the body to the header.

Advertising tracking at its best.

~~~
colanderman
Your issue is not with 103 but with _Link: rel=preload_ [1], which has been
supported in Chrome for over a year [2].

[1] [https://w3c.github.io/preload/](https://w3c.github.io/preload/)

[2]
[https://bugs.chromium.org/p/chromium/issues/detail?id=471199](https://bugs.chromium.org/p/chromium/issues/detail?id=471199)

~~~
est
Thanks for the link.

I think this is the case where X is innocent and Y is innocent, but combined X
and Y leaves a huge exploitable surface.

------
toast0
This seems like it's only of interest if you need to do time consuming
processing before deciding what http status code to send, because once you
pick the status code, you can send it and start sending the rest of the
headers.

Is it common to serve something other than http 200 and to take a long time to
do it -- but also be able to send something early sooner? Most of the the slow
pages I've worked on were either slow to load all the details, but I knew it
was 200 near instantly, and could get all the headers and the HTML head out
pretty quickly, or had some structural problems that made them terribly slow,
but we couldn't get anything out quickly anyway.

~~~
xxs
The vast majority of the cases the status would be planned as 200... unless
for whatever reason a timeout/error occurs during process. Then it may end up
with error status and or redirect to an error handling page.

Still very dubious proposal with minimal benefit(s).

------
tyingq
Shouldn't it have a complimentary "denied" code? Or is 403 "Forbidden" the
complement? Or maybe 401 "Unauthorized"?

I guess it is related to preloading? Not quite getting the purpose.

Edit: ahh, the complete story is here: [https://tools.ietf.org/html/draft-
ietf-httpbis-early-hints-0...](https://tools.ietf.org/html/draft-ietf-httpbis-
early-hints-05) (fixed). Note that someone fixed the capitalized "A" in
"approved" in the title. It confused my initial reading of the article.

~~~
itaris
Someone only read the headline.

~~~
tyingq
I read the headline and it colored my reading of the linked article. Bad
headlines set up bad context. Someone also lowercased the "a" in approved in
the headline since I saw it.

Someone else suggested [https://tools.ietf.org/html/draft-ietf-httpbis-early-
hints-0...](https://tools.ietf.org/html/draft-ietf-httpbis-early-hints-05),
which has more context without a need for a second click.

------
psadri
Browsers can learn this on their own and start prefetching likely resources
before the server responds. Like a cpu's branch prediction. I wouldn't be
surprised if chrome already did this.

------
photonios
This is actually great. I ran into this problem recently. I wanted to push out
some CSS while waiting for some external response. Unfortunately, it was very
important to be able to send a 404 in case it turned out the external
response. This means we had to live with a higher TFFB.

Let’s hope this becomes widely adopted soon. Does anyone know how long it took
till other HTTP features that were added later (not HTTP2) were widely
supported?

------
acdha
Has anyone looked into implementing this with Varnish? It seems like a great
way to handle non-streaming backend servers.

------
bluetwo
Neat. Now how is this going to get abused?

~~~
bluetwo
For instance, you could add this to a popular site to create a really easy
DDOS attack on the target of your choice.

~~~
TheCoreh
You could also add <link rel="stylesheet"> tags to achieve a similar effect,
so it doesn't really introduce anything new

~~~
bluetwo
It would be far more hidden in the headers.

------
andy_ppp
Nice, not sure how trivial this will be to add to frameworks and still make it
not as slow as two requests. I think maybe this could be a macro in Elixir’s
Phoenix framework that’s done at compilation time; would mean it isn’t
changeable at runtime but would make it very fast.

------
jijji
why not just add a "preload" flag to the style or link tag, this looks like a
lot of extra work.

------
_justinfunk
Title should probably be changed to have a lowercase "approved". It's a little
misleading as-is.

~~~
scott_karana
Agreed! I was confused when it was

    
    
      103 Early Hints
    

Not

    
    
      103 Approved

------
grandalf
This seems to be meant as an AMP killer.

------
fiatjaf
Isn't this way too complicated to define and implement considering that HTTP/2
is already out there?

~~~
arcbyte
As someone who thinks HTTP2 is garbage and refuses to support it in my
enterprise, this is a good step towards a MUCH better solution.

~~~
avg_dev
I don't know much about HTTP2; why do you dislike it?

~~~
merb
one reason to dislike it, is because it is way too stateful. and a lot of
things depends on timings. /and the spec misses some stuff, like if all my
streams are done (except one long running stream, SSE, etc) and I get a new
request what do do now? (there are multiple ways of doing that, one would be
to just cancel the long running stream and hope that the client reconnects to
the new connection).

Personally I think it is more complex than it was needed to be. stream
priority can also be a PITA.

Ah and I basically forgot, due to the state it is practically not the best
protocol for mobile clients over 2G/low end 3G.

It's not the worst, I think h2 would be fine if it would be 10 years earlier,
since I think we can learn a lot from the behavior of h2 in real world
scenarios and than use the best things from http/1.1 and h2 to make h3.

~~~
styfle
I thought HTTP2 was born out of the experimental SPDY protocol which was used
in the wild for quite some time, maybe 5 years. I think SPDY was released
around 2010.

HTTP2 is like the good parts of SPDY and what was learned over that time
period (I think).

~~~
merb
SPDY wasn't in widespread use (I think it had something like 30% usage),
especially not in mobile environments. basically I also did not say that
h2/spdy is bad, it actually solves some things, but makes others worse. it
actually solves head of line blocking. multiplexing is cool, but comes with
the overhead of a connection state.

I think h2 is perfect for RPC like architectures, interconnection between
servers is a perfect fit for something like h2. but when it comes to real
world traffic it depends on too many parameters. The sad part is, is that h2
will take forever to gain that data and to have widespread usage. the hard
part will be 100% h2, a lot of stuff prolly only uses h2 at the edge.

Also most flaws of h2, will probably be fixed without a new protocol, i.e. in
the next 10 years I hope that _all_ mobile environments have a widespread use
of 3g networks (or to say it differently, I dream of) and hopefully all
network people make world wide latency better and make all networks more
reliable.

------
avg_dev
Wow, I thought I knew basic HTTP but I don't know anything about preloading or
prefetching or early hints. Time to read up.

~~~
grzm
Don't beat yourself up _too_ much. The document is marked "2017-10-30 (latest
revision 2017-10-28)".

