HTTP 103 Early Hints

HTTP 103 Early Hints is an informational response that allows the server to send preliminary headers before the final response, primarily used to let the browser start preloading resources (CSS, JS, fonts) while the server is still generating the full response. Defined in RFC 8297, it enables servers to send Link headers with rel=preload or rel=preconnect hints, shaving hundreds of milliseconds off page load times by parallelizing resource fetching with server-side processing.

Debug HTTP 103 live
Analyze real 103 behavior — headers, caching, CORS, redirects
Open Inspector →

Try it (live endpoint)

Response includes the status code, standard headers (including Content-Type), and a small diagnostic JSON body describing the request and returned status.

Simulator URL (copy in the app after load — not a normal link):

https://httpstatus.com/api/status/103

Example request:

curl -i "https://httpstatus.com/api/status/103"
Try in playground

Meaning

Used to return preliminary response headers (typically Link headers for preloading resources) before the final HTTP response.

What it guarantees
  • An interim response was provided before the final response.
What it does NOT guarantee
  • The request has succeeded or failed.
  • Clients should treat this as a final outcome.

When to use this status

  • Use only when it accurately describes the outcome for clients and tooling.

When NOT to use this status (common misuses)

Critical headers that matter

Tool interpretation

Browsers
Treats as success; caches/revalidates based on headers and validators.
API clients
Deserializes per Content-Type; conditional requests use validators when implemented.
Crawlers / SEO tools
Indexes depending on headers and canonical stability; caches behavior via validators and cache directives.
Uptime monitors
Typically marks success; advanced checks may flag header anomalies or latency.
CDNs / reverse proxies
Caches/revalidates based on Cache-Control, ETag, and Vary; compression and content-type affect behavior.

Inspector preview (read-only)

On this code, Inspector focuses on semantics, headers, and correctness warnings that commonly affect clients and caches.

Signals it will highlight
  • Status semantics vs method and body expectations
  • Header sanity (Content-Type, Cache-Control, Vary) and evidence completeness
Correctness warnings
No common correctness warnings are specific to this code.

Guided Lab outcome

  • Reproduce HTTP 103 Early Hints using a controlled endpoint and capture the full exchange.
  • Practice distinguishing status semantics from transport issues (redirects, caching, proxies).

Technical deep dive

103 Early Hints was standardized in RFC 8297 to address the 'server think time' gap — the period between the server receiving a request and sending the response during which the browser sits idle. The server sends a 103 response containing Link headers (e.g., Link: </style.css>; rel=preload; as=style) and the browser immediately begins fetching those resources. When the final 200 response arrives, the resources may already be cached. Key constraints: only Link headers are useful in 103 responses; other headers like Set-Cookie are ignored. The final response's headers take precedence. Chrome 103+ and Firefox 102+ support Early Hints. HTTP/2 Server Push was the previous solution but is being deprecated in favor of 103 because Early Hints respects the browser's cache and prioritization decisions.

Real-world examples

Dynamic page with static assets
A server-rendered page takes 500ms to generate (database queries, template rendering). The server immediately sends 103 with Link headers for the page's CSS, JavaScript, and web fonts. The browser fetches these assets in parallel with the server's 500ms processing time, reducing perceived load time by up to 500ms.
CDN-level early hints
Cloudflare's Early Hints feature caches the 103 response at the edge. When a user requests a page, the CDN immediately sends cached Link headers from the previous 200 response's Link headers, then proxies the request to the origin. The browser preloads resources while waiting for the origin response.
E-commerce product page
A product page requires a database lookup, inventory check, and price calculation (300ms). The server sends 103 with preload hints for the product image, cart JavaScript, and fonts. By the time the 200 response arrives, critical resources are already downloaded or in-flight.

Framework behavior

Express.js (Node)
Express doesn't have built-in 103 support. Use res.writeEarlyHints() (Node.js 18.11+): res.writeEarlyHints({ link: '</style.css>; rel=preload; as=style, </app.js>; rel=preload; as=script' }); then continue processing and send the final response normally.
Django / DRF (Python)
Django 4.2+ doesn't natively support 103. With an ASGI server (Uvicorn/Daphne), you could send raw 103 responses, but it's easier to rely on a CDN (Cloudflare) or reverse proxy (nginx) to handle Early Hints based on Link headers from previous responses.
Spring Boot (Java)
Spring Boot doesn't have built-in 103 support. For Tomcat, this requires custom Valve implementation. The practical approach is to configure nginx or your CDN to emit 103 Early Hints based on Link headers from your 200 responses.
FastAPI (Python)
FastAPI/Starlette doesn't natively support 103. The recommended approach is to use a reverse proxy: nginx 1.25.1+ supports 'early_hints on;' which automatically sends 103 based on Link headers in the upstream response.

Debugging guide

  1. Use Chrome DevTools → Network tab → check the 'Protocol' column; 103 responses appear as separate entries before the final 200
  2. In Chrome, navigate to chrome://net-export/ to capture a network log that includes 103 responses
  3. Verify your CDN/proxy isn't stripping 103 responses — some older load balancers don't forward 1xx responses
  4. Check browser support: Chrome 103+, Firefox 102+, Safari (not yet as of 2024) — test with feature detection
  5. Test with: curl -v --http1.1 https://example.com — 103 responses will show in the verbose output between the request and the final response

Code snippets

Node.js
const http = require('http');

const server = http.createServer(async (req, res) => {
  // Send 103 Early Hints immediately
  res.writeEarlyHints({
    'link': [
      '</style.css>; rel=preload; as=style',
      '</app.js>; rel=preload; as=script',
      '</font.woff2>; rel=preload; as=font; crossorigin',
    ]
  });

  // Simulate server processing time
  const data = await fetchFromDatabase();
  const html = renderTemplate(data);

  // Send final response
  res.writeHead(200, { 'Content-Type': 'text/html' });
  res.end(html);
});
Python
# nginx config for Early Hints (most practical approach)
# Since Python frameworks lack native 103 support,
# configure nginx to handle it:
#
# server {
#     listen 443 ssl http2;
#     location / {
#         proxy_pass http://backend;
#         # nginx 1.25.1+ early hints
#         add_header Link '</style.css>; rel=preload; as=style';
#         add_header Link '</app.js>; rel=preload; as=script';
#     }
# }

# Python app just includes Link headers in response
from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()

@app.get('/')
async def index():
    return HTMLResponse(
        content=render_page(),
        headers={'Link': '</style.css>; rel=preload; as=style'}
    )
Java (Spring)
// Spring Boot - return Link headers for proxy-based 103
@GetMapping("/")
public ResponseEntity<String> index() {
    String html = renderPage(); // slow operation
    return ResponseEntity.ok()
        .header("Link",
            "</style.css>; rel=preload; as=style",
            "</app.js>; rel=preload; as=script")
        .contentType(MediaType.TEXT_HTML)
        .body(html);
}
// Configure nginx upstream to convert Link headers
// to 103 Early Hints responses
Go
func handler(w http.ResponseWriter, r *http.Request) {
	// Go 1.20+ supports Early Hints
	w.Header().Add("Link", "</style.css>; rel=preload; as=style")
	w.Header().Add("Link", "</app.js>; rel=preload; as=script")
	w.WriteHeader(http.StatusEarlyHints) // 103

	// Perform slow server-side work
	data := fetchFromDB()
	html := renderTemplate(data)

	// Send final response
	w.Header().Set("Content-Type", "text/html")
	w.WriteHeader(http.StatusOK)
	w.Write([]byte(html))
}

FAQ

How is 103 Early Hints different from HTTP/2 Server Push?
Server Push was server-initiated — the server decided what to push without the browser's input, often pushing resources the browser already had cached. 103 Early Hints sends hints that the browser evaluates — it can skip preloading if the resource is cached, apply its own prioritization, and use its standard fetch logic. This makes Early Hints more cache-friendly and less bandwidth-wasteful. Chrome removed Server Push support in 2022.
Can 103 Early Hints include Set-Cookie or other headers?
Browsers are required to ignore all headers in 103 responses except Link headers. The RFC recommends servers only include Link headers. Set-Cookie, Content-Security-Policy, and other headers in 103 are silently discarded. Only the final response's headers are authoritative.
Does 103 work with HTTP/1.1 or only HTTP/2?
103 Early Hints works with both HTTP/1.1 and HTTP/2. However, it's most effective with HTTP/2 where the preloaded resources can be fetched in parallel on the same connection. With HTTP/1.1, the browser may still benefit but is limited by the 6-connections-per-host limit.
What happens if the 103 hints don't match the final response?
The browser treats 103 as advisory. If the final response doesn't reference a preloaded resource, the browser simply caches it normally. If the final response redirects (301/302), the preloaded resources for the original URL may be wasted bandwidth. CDNs mitigate this by only caching 103 hints from 200 responses.

Client expectation contract

Client can assume
    Client must NOT assume
    No common correctness warnings are specific to this code.
    Retry behavior
    Retries are generally unnecessary; treat as final unless domain rules require revalidation.
    Monitoring classification
    Server error
    Use payload and header checks to avoid false positives; cacheability depends on Cache-Control/ETag/Vary.

    Related status codes

    102 Processing
    The server has received and is processing the request, but no response is available yet. Used in WebDAV to prevent client timeouts on long operations.
    200 OK
    The request has succeeded.

    Explore more

    Related guides
    Related tools
    Related utilities