# Early-Hints

> Learn how 103 Early Hints allows servers to send preload hints while preparing the main response. Improve page load performance with early resource loading.

Source: https://howhttpworks.com/headers/early-hints
Last reviewed: 2026-10-05

> **TL;DR:** Early Hints is the `103` status, not a header. The server sends it while it's still building the page, with `Link` headers that tell the browser to preload a stylesheet or preconnect to an origin. Browsers can ignore 103, so repeat the same links in the final response or HTML.

## What is Early-Hints?

Server think time is wasted time for the browser. [RFC 8297](https://www.rfc-editor.org/rfc/rfc8297) defines `103` to fill that gap: the server sends some headers early, and the browser can start fetching a hinted stylesheet before the HTML arrives. A 103 has no body, and it doesn't commit the server to anything. The final response can have a different status and different headers.

## Example

Here's a trimmed HTTP/1.1 exchange where the connection closes after the HTML:

```http
HTTP/1.1 103 Early Hints
Link: </style.css>; rel=preload; as=style

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Link: </style.css>; rel=preload; as=style
Connection: close

<!doctype html><link rel="stylesheet" href="/style.css"><p>Hello</p>
```

The blank line ends the 103. The HTML belongs to the final `200`, which repeats the `Link` header for clients that skipped the 103.

## What to Send in 103

Hint resources you know every response for that page needs, before you've generated it: the main stylesheet, a critical font, the origin your API lives on. For a stylesheet, `as=style` tells the browser what it's fetching. [Font preloads](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/preload#cors-enabled_fetches) need `crossorigin` to match the CORS fetch the page will make later, even for same-origin fonts. Check that each hint gets used before adding more.

## Implementation

On Cloudflare Workers, you don't build the 103 yourself. Enable Early Hints for the zone and return preload links in the final response, as in [Cloudflare's Worker example](https://developers.cloudflare.com/workers/examples/103-early-hints/):

```javascript
export default {
  async fetch() {
    return new Response(
      '<!doctype html><link rel="stylesheet" href="/style.css"><p>Hello</p>',
      {
        headers: {
          'Content-Type': 'text/html; charset=utf-8',
          Link: '</style.css>; rel=preload; as=style',
        },
      },
    );
  },
};
```

Serve `/style.css` separately. Cloudflare caches the `Link` headers from this response and uses them to send 103s on later requests, so the request that sets the header won't necessarily get a 103 itself.

In Node, [`response.writeEarlyHints()`](https://nodejs.org/api/http.html#responsewriteearlyhintshints-callback) sends the 103 directly. This server sends the hint, then reads the page from disk, and also serves the stylesheet it hinted:

```javascript
import http from 'node:http';
import { readFile } from 'node:fs/promises';

http.createServer(async (req, res) => {
  if (req.url === '/style.css') {
    res.writeHead(200, { 'Content-Type': 'text/css' });
    res.end('body { font-family: sans-serif; }');
    return;
  }
  if (req.url !== '/') {
    res.writeHead(404);
    res.end();
    return;
  }
  const link = '</style.css>; rel=preload; as=style';
  res.writeEarlyHints({ link });
  try {
    const html = await readFile(new URL('./index.html', import.meta.url));
    res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', Link: link });
    res.end(html);
  } catch {
    res.writeHead(500);
    res.end();
  }
}).listen(8084, '127.0.0.1');
```

Save it as `hints-demo.mjs` next to an `index.html` that links `/style.css`, then run `node hints-demo.mjs`. `curl -sS -D - http://127.0.0.1:8084/` prints both response blocks. Delete `index.html` and you'll see a 103 followed by a 500: the hint goes out before the server knows whether the page will work.

## Implementation Notes

[Cloudflare's zone implementation](https://developers.cloudflare.com/cache/advanced-configuration/early-hints/) only sends 103 over HTTP/2 and HTTP/3, never HTTP/1.1. It handles preload and preconnect links, and skips the 103 when the final response is already available. It only generates hints for URIs ending in `.html`, `.htm`, `.php`, or with no extension, where the final status is 200, 301, or 302.

Cloudflare Pages has a [separate integration](https://developers.cloudflare.com/pages/configuration/early-hints/) that builds hints from `<link>` elements in your HTML. A Cloudflare zone reads `Link` headers instead, so check what your CDN actually looks at.

For browser support, see [MDN's compatibility table](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/103#browser_compatibility).

## Common Mistakes

The most common mistake is sending an `Early-Hints: ...` header, which does nothing. The second is putting `Link` only in the 103, so clients that ignore 103 never see the hint. Also, Cloudflare keys hints by URI without the query string and can send them before authentication runs, so keep sensitive URLs out of hints that every visitor shares.

To inspect the edge response, use an HTTP/2-capable curl build:

```bash
curl --http2 -sS -D - -o /dev/null https://example.com/
```

Look for a 103 header block before the final status. Then open the browser's Network panel to confirm the hinted resource was fetched early and what started the fetch.

## Early Hints vs HTTP/2 Server Push

Early Hints sends only metadata, and the browser decides whether to fetch. HTTP/2 server push sent the resource itself on a separate stream, whether or not the browser already had it cached. With 103 you only win if a fetch the page needs starts earlier than it otherwise would.

## Related Headers

- [Link](https://howhttpworks.com/headers/link)
- [Server-Timing](https://howhttpworks.com/headers/server-timing)
- [103 Early Hints](https://howhttpworks.com/status-codes/103)
