- Home
- HTTP Status Codes
- 103 Early Hints
Status Code
103 Early Hints
The server sends preliminary response headers to help the client start preloading resources. Learn how 103 Early Hints improves page load performance.
What is 103 Early Hints?
TL;DR: Server sends resource hints before final response to speed up page loading. Browser can start downloading CSS/JS while waiting.
A 103 Early Hints status code is an informational response that lets the server send preliminary HTTP headers while it’s still preparing the full response. Think of it like a restaurant server bringing you bread and water while the kitchen prepares your main course—it keeps you occupied and improves the overall experience.
This status code is primarily used to hint at resources the client should start preloading (like CSS, JavaScript, fonts) before the final HTML response is ready, significantly improving page load performance.
When Does This Happen?
You’ll see a 103 Early Hints response in these common situations:
1. Server-Side Rendering (SSR)
Server generating HTML (takes 200ms)
→ Sends 103 with CSS/JS hints immediately
→ Browser starts downloading while waiting
→ Final HTML arrives with resources already loading
2. Database-Heavy Pages
Querying multiple databases for content
→ Send 103 with known static assets
→ Database queries complete
→ Return final response
3. Authenticated Pages
Validating user session (slow)
→ Send 103 with common resources
→ Complete auth check
→ Return personalized content
4. API-Dependent Pages
Waiting for third-party API responses
→ Send 103 with static assets
→ APIs respond
→ Render final page
5. Edge Computing
CDN edge processing request
→ Sends 103 with resource hints
→ Origin server generates response
→ Returns final content
Example Responses
Basic Early Hints:
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
Content-Type: text/html
Content-Length: 1234
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/style.css">
<script src="/script.js"></script>
</head>
<body>...</body>
</html>
```text
**Multiple Resources:**
```http
HTTP/1.1 103 Early Hints
Link: </critical.css>; rel=preload; as=style
Link: </app.js>; rel=preload; as=script
Link: </font.woff2>; rel=preload; as=font; crossorigin
Link: </hero.jpg>; rel=preload; as=image
Link: https://cdn.example.com; rel=preconnect
HTTP/1.1 200 OK
Content-Type: text/html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/critical.css">
<link rel="preload" href="/font.woff2" as="font" crossorigin>
</head>
<body>
<img src="/hero.jpg" alt="Hero">
<script src="/app.js"></script>
</body>
</html>
With CSP Headers:
HTTP/1.1 103 Early Hints
Link: </style.css>; rel=preload; as=style
Link: </main.js>; rel=preload; as=script
Content-Security-Policy: default-src 'self'
HTTP/1.1 200 OK
Content-Type: text/html
Content-Security-Policy: default-src 'self'
Content-Length: 5678
<!DOCTYPE html>
<html>...</html>
```text
## Real-World Example
Imagine you're serving a Next.js application with server-side rendering:
**Client Request:**
```http
GET /dashboard HTTP/1.1
Host: app.example.com
User-Agent: Mozilla/5.0...
Cookie: session=abc123
Early Hints Response (sent immediately):
HTTP/1.1 103 Early Hints
Link: </_next/static/css/main.css>; rel=preload; as=style
Link: </_next/static/js/main.js>; rel=preload; as=script
Link: </_next/static/js/webpack.js>; rel=preload; as=script
Link: </fonts/inter.woff2>; rel=preload; as=font; crossorigin
Link: https://cdn.example.com; rel=preconnect
Link: https://api.example.com; rel=preconnect
```text
**Browser starts preloading resources...**
**Final Response (200ms later):**
```http
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: private, no-cache
Content-Length: 45678
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/_next/static/css/main.css">
<link rel="preconnect" href="https://cdn.example.com">
<link rel="preload" href="/fonts/inter.woff2" as="font" crossorigin>
</head>
<body>
<div id="__next">
<div class="dashboard">
<!-- Rendered dashboard content -->
</div>
</div>
<script src="/_next/static/js/webpack.js"></script>
<script src="/_next/static/js/main.js"></script>
</body>
</html>
103 vs Other Informational Codes
| Code | Meaning | Purpose | Browser Action |
|---|---|---|---|
| 103 | Early Hints | Performance optimization | Start preloading resources |
| 100 | Continue | Upload optimization | Send request body |
| 101 | Switching Protocols | Protocol upgrade | Switch to new protocol |
| 102 | Processing | Long operation status | Wait for completion |
Important Characteristics
Timing Matters:
Request received → Immediately send 103 → Process request → Send final response
(0-10ms) (50-500ms)
Link Header Format:
Link: <URL>; rel=preload; as=TYPE; [additional-params]
Examples:
Link: </style.css>; rel=preload; as=style
Link: </font.woff2>; rel=preload; as=font; crossorigin
Link: https://cdn.com; rel=preconnect
```text
**Multiple 103 Responses:**
- Servers can send multiple 103 responses
- Each can contain different hints
- Useful when hints are discovered progressively
**Compatibility:**
```http
Supported: Chrome 103+, Edge 103+, Firefox (partial)
Unsupported: Safari (as of early 2026)
Fallback: Browsers ignore 103, wait for final response
```text
## Common Mistakes
**❌ Hinting resources not in final response**
```http
103 Early Hints:
Link: </unused.css>; rel=preload; as=style ← Never used
200 OK:
<html>
<!-- unused.css is never referenced -->
</html>
❌ Sending 103 too late
// Bad: After processing is done
await processRequest() // 500ms
res.writeEarlyHints() // Too late!
res.send(html)
```text
**❌ Overloading with hints**
```http
HTTP/1.1 103 Early Hints
Link: </1.css>; rel=preload; as=style
Link: </2.css>; rel=preload; as=style
Link: </3.css>; rel=preload; as=style
... 50 more resources ... ← Too many!
✅ Correct usage
// Good: Send immediately, only critical resources
app.get('/page', async (req, res) => {
// Send early hints ASAP
res.writeEarlyHints({
link: ['</critical.css>; rel=preload; as=style', '</app.js>; rel=preload; as=script']
})
// Then process request
const data = await fetchData()
res.send(renderHTML(data))
})
```text
## Best Practices
**Hint Only Critical Resources:**
```http
HTTP/1.1 103 Early Hints
Link: </critical.css>; rel=preload; as=style ✓ Above-fold CSS
Link: </hero.jpg>; rel=preload; as=image ✓ LCP image
Link: </analytics.js>; rel=preload; as=script ✗ Non-critical
Send Early Hints ASAP:
// Express.js example
app.get('/dashboard', async (req, res) => {
// Send hints before ANY processing
res.writeEarlyHints({
link: [
'</static/main.css>; rel=preload; as=style',
'</static/app.js>; rel=preload; as=script',
'https://fonts.googleapis.com; rel=preconnect'
]
})
// Now do expensive operations
const userData = await db.query('SELECT * FROM users...')
const analytics = await fetchAnalytics()
res.send(renderPage(userData, analytics))
})
```javascript
**Consistent Hints Across Requests:**
```javascript
// Cache hints for similar pages
const dashboardHints = [
'</dashboard.css>; rel=preload; as=style',
'</dashboard.js>; rel=preload; as=script'
]
app.get('/dashboard/*', (req, res) => {
res.writeEarlyHints({ link: dashboardHints })
// ... rest of handler
})
Monitor Performance Impact:
// Track Early Hints effectiveness
const start = Date.now()
res.writeEarlyHints({ link: hints })
res.on('finish', () => {
const duration = Date.now() - start
metrics.record('early_hints.duration', duration)
})
```javascript
## Implementation Examples
**Node.js (Native HTTP):**
```javascript
const http = require('http')
http
.createServer((req, res) => {
// Send 103 Early Hints
res.writeEarlyHints({
link: ['</style.css>; rel=preload; as=style', '</script.js>; rel=preload; as=script']
})
// Simulate processing time
setTimeout(() => {
res.writeHead(200, { 'Content-Type': 'text/html' })
res.end(`
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<h1>Hello World</h1>
<script src="/script.js"></script>
</body>
</html>
`)
}, 100)
})
.listen(3000)
Express.js:
const express = require('express')
const app = express()
app.get('/', async (req, res) => {
// Send early hints immediately
if (res.writeEarlyHints) {
res.writeEarlyHints({
link: [
'</public/main.css>; rel=preload; as=style',
'</public/app.js>; rel=preload; as=script',
'https://fonts.gstatic.com; rel=preconnect; crossorigin'
]
})
}
// Expensive operation
const data = await fetchDataFromDB()
res.send(renderHTML(data))
})
```javascript
**Cloudflare Worker:**
```javascript
addEventListener('fetch', (event) => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
// Send Early Hints
const earlyHintsResponse = new Response(null, {
status: 103,
headers: {
Link: ['</style.css>; rel=preload; as=style', '</app.js>; rel=preload; as=script'].join(', ')
}
})
// Note: Cloudflare automatically handles 103
// This is for illustration
// Fetch from origin
const response = await fetch(request)
return response
}
Nginx Configuration:
# Nginx doesn't natively support 103, but can be added via modules
# or handled by upstream application server
location / {
# Application sends 103
proxy_pass http://backend;
proxy_http_version 1.1;
}
```text
## Performance Impact
**Before Early Hints:**
```text
Request → Wait 200ms → Receive HTML → Parse → Discover resources → Download
↑
Start at ~200ms
```text
**With Early Hints:**
```text
Request → 103 (5ms) → Start downloads → Wait 195ms → Receive HTML
↑ ↑
Discover resources Resources already loading
Typical Improvements:
- First Contentful Paint (FCP): 10-20% faster
- Largest Contentful Paint (LCP): 15-30% faster
- Time to Interactive (TTI): 10-20% faster
Try It Yourself
Visit our request builder and see 103 Early Hints in action:
- Set method to GET
- Set path to /early-hints-demo
- Click Send request
- Watch for 103 response with Link headers
- Observe resource preloading before final response
Related Status Codes
- 100 Continue - Proceed with request body
- 200 OK - Final successful response
- Link Header (not a status) - Resource relationship hints
- Server-Timing Header - Performance metrics
Frequently Asked Questions
What does 103 Early Hints mean?
A 103 Early Hints response lets the server send Link headers before the final response, allowing browsers to preload critical resources like CSS and fonts while waiting for the main response.
How does 103 Early Hints improve performance?
It reduces page load time by letting browsers start fetching critical resources before the server finishes generating the HTML. This parallelizes resource loading with server processing.
Which browsers support 103 Early Hints?
Chrome 103+, Edge 103+, and Firefox 102+ support Early Hints. Safari support is limited. Always test browser compatibility for your target audience.
How do I implement 103 Early Hints?
Configure your server or CDN to send 103 responses with Link headers for critical resources. Many CDNs like Cloudflare support automatic Early Hints generation.