HTTP 103 Early Hints - some not being sent

Howdy forum!

I’ve flipped on Early Hints for our site, and they do appear to be working. What’s confusing to me, though, is that it’s only sending a subset of our Link header as early hints, and not every resource specified in the Link header.

The page is https://app.gusto.com/login.

Using @M4rt1n 's wonderful tool https://code103.hotmann.de/, you can see that the 103 Early Hint contains 3 resources (3 webfont files).

However, the problem is that the Link header on the 200 response actually contains 2 more resources (2 images):

 <https://d3bnlkto289wdc.cloudfront.net/assets/packs/media/illustrations/illo-people-with-bouquet-a88253a7.svg>; rel=preload; as=image; type=image/svg+xml,<https://d3bnlkto289wdc.cloudfront.net/assets/packs/media/illustrations/illo-2-person-scene-01ac18a3.svg>; rel=preload; as=image; type=image/svg+xml,

So what gives? Does Cloudflare have an algorithm for determining what it thinks should be included in a 103 and what isn’t? The 2 images definitely are included in a valid way because Chrome acts on them as soon as it gets the 200 response back.

1 Like

Thanks for using my tool! Nice to hear it is useful for others :slight_smile:

Very good decision on dynamic pages.

Yes, that’s what I see after some manual checks as well. I have also debugged my tools again to see if it is properly working and it is. That’s the actual result the tool gets.

I can see they are in the link-header, but I can not confirm that I see these two additional headers being fired as an early hint.

Could be multiple things.

  1. I noticed that when I debug it manually and request the whole response header I first get the HTTP/2 103 (Early Hint) then afterwards a HTTP/2 403 (Forbidden). Do you trigger less link-headers in case of a 403?
    Why does the 403 happen? My application is go-based, but therefore was blocked by Cloudflare and as a Workaround I made it identify as curl - ‘curl/h0tmann’ to be precise. Curl mostly is getting rated as “bot”, or “crawler” and therefore forbidden, specially on logins.
    So the question is: where is it getting blocked and can you turn it off for testing purposes?
    Maybe less link-headers are getting set when a 403 is triggered. If so we see 5 link-headers in the browser, but just 3 when requested by my tool.
    BTW: same result with curl itself.
    Beside that something might block curl it also could be my IP, which is a server-ip which therefore might get blocked.
  2. Cloudflare might choose which resources will benefit from getting triggered as early hints, but I am not aware of any of those practices. But I also have to say: things change quickly and ATM my ressources are very short, so bearly no time to get informed.
  3. something else, I have not considered.

Here the command for curl, so you can test on your own from other IPs:

$ curl -X HEAD -I https://app.gusto.com/login

result should be something like this:

HTTP/2 103
link: <https://d3bnlkto289wdc.cloudfront.net/assets/packs/media/fonts/gcentra-book-ebf32d29.woff2>; as=font; crossorigin=anonymous; rel=preload; type=font/woff2, <https://d3bnlkto289wdc.cloudfront.net/assets/packs/media/fonts/gcentra-medium-cb005fbb.woff2>; as=font; crossorigin=anonymous; rel=preload; type=font/woff2, <https://d3bnlkto289wdc.cloudfront.net/assets/packs/media/gusto-icons/gusto-icons-4b9f77a9.woff2>; as=font; crossorigin=anonymous; rel=preload; type=font/woff2

HTTP/2 403
date: Wed, 09 Nov 2022 18:44:48 GMT
content-type: text/html; charset=UTF-8
cf-chl-bypass: 1
referrer-policy: same-origin
permissions-policy: accelerometer=(),autoplay=(),camera=(),clipboard-read=(),clipboard-write=(),fullscreen=(),geolocation=(),gyroscope=(),hid=(),interest-cohort=(),magnetometer=(),microphone=(),payment=(),publickey-credentials-get=(),screen-wake-lock=(),serial=(),sync-xhr=(),usb=()
cache-control: private, max-age=0, no-store, no-cache, must-revalidate, post-check=0, pre-check=0
expires: Thu, 01 Jan 1970 00:00:01 GMT
x-frame-options: SAMEORIGIN
set-cookie: __cf_bm=sbMxA8VCBWCSHSfIAaCqyOMf7oaDlzXPt1sOth7XzWk-1668019488-0-AVYa11dg2Fpcd0tvA7bRX80r1OqXcmtVzXy8p3hfYXhbo21RtMbiWmavoUufg5OJ/V9ZEKA6zD/I6E69A4UUZjXKjPuBAXbS0WzA67Pc1wJa; path=/; expires=Wed, 09-Nov-22 19:14:48 GMT; domain=.gusto.com; HttpOnly; Secure; SameSite=None
strict-transport-security: max-age=31536000; includeSubDomains; preload
server: cloudflare
cf-ray: 7678b3ac0e5868fe-FRA
alt-svc: h3=":443"; ma=86400, h3-29=":443"; ma=86400

I assume the request was blcked by Cloudflare (check Firewall etc.) as in the headers no cf-cache-status appears, but in the browser I see: cf-cache-status: DYNAMIC

Hope that helps to debug it a little.

Interesting. I don’t think it’s option 1, because we send no link headers at all in the case of a 403.

So, Cloudflare must be applying some logic to what Link headers are turned into 103 responses that they haven’t yet publicly documented.