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.
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"Used to return preliminary response headers (typically Link headers for preloading resources) before the final HTTP response.
On this code, Inspector focuses on semantics, headers, and correctness warnings that commonly affect clients and caches.
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.
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);
});# 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'}
)// 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 responsesfunc 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))
}