HTTP/2 Server Push Issues
What This Means
HTTP/2 Server Push allows servers to send resources to the browser before they're requested, potentially improving page load times. However, incorrect implementation can actually harm performance by pushing resources the browser already has cached or doesn't need.
How Server Push Works
Normal Process:
- Browser requests HTML
- Server sends HTML
- Browser parses HTML
- Browser requests CSS, JS, images
- Server sends resources
With Server Push:
- Browser requests HTML
- Server sends HTML + pushes CSS, JS immediately
- Browser receives resources before parsing HTML
- Faster rendering (when done correctly)
Important Note About Server Push in 2025
Chrome has removed support for HTTP/2 Server Push as of version 106 (2022). Other browsers are following suit.
Why It's Being Deprecated:
- High complexity, low adoption
- Easy to misconfigure and harm performance
- Better alternatives exist (preload, early hints)
- Caching issues difficult to solve
Modern Alternatives:
<link rel="preload">- Better browser support- 103 Early Hints - New standard, better design
- HTTP/3 doesn't include server push
- Resource prioritization via Priority Hints
Impact on Your Business
When Misconfigured:
- Wasted bandwidth from pushing cached resources
- Slower page loads from over-pushing
- Increased server load
- Cache invalidation problems
Modern Best Practice:
- Remove server push configurations
- Use preload links instead
- Implement 103 Early Hints where available
- Focus on resource prioritization
Common Causes
Over-Pushing Resources
# Wrong: Pushing too many resources
Link: </css/style.css>; rel=preload; as=style
Link: </js/script1.js>; rel=preload; as=script
Link: </js/script2.js>; rel=preload; as=script
Link: </js/script3.js>; rel=preload; as=script
Link: </images/hero.jpg>; rel=preload; as=image
Link: </images/logo.png>; rel=preload; as=image
Link: </fonts/font.woff2>; rel=preload; as=font
# Result: Bandwidth waste, slow connection saturation
Pushing Cached Resources
# Wrong: No cache-awareness
location / {
http2_push /css/style.css;
http2_push /js/app.js;
}
# Problem: Pushes even if browser has cached versions
Pushing Wrong Resources
# Wrong: Pushing non-critical resources
Link: </analytics.js>; rel=preload; as=script
Link: </ads.js>; rel=preload; as=script
# Problem: Delays critical resource delivery
Push Without HTTP/2
# Configuration exists but HTTP/2 not enabled
http2_push /style.css;
# No effect without HTTP/2
How to Diagnose
Method 1: Chrome DevTools Network Tab
Note: Server Push removed in Chrome 106+, but you can check headers:
- Open DevTools (
F12) - Go to Network tab
- Reload page
- Click on HTML document
- Check Response Headers for
Link:headers withrel=preload
What to Look For:
Link: </css/style.css>; rel=preload; as=style
Link: </js/app.js>; rel=preload; as=script
Method 2: Check HTTP/2 Status
- Open DevTools Network tab
- Right-click column headers
- Enable "Protocol" column
- Look for "h2" (HTTP/2) or "h3" (HTTP/3)
What to Look For:
- Protocol: h2 (HTTP/2 enabled)
- Protocol: http/1.1 (HTTP/2 not enabled)
- Protocol: h3 (HTTP/3, no push support)
Method 3: WebPageTest
- Visit WebPageTest.org
- Enter your URL
- Run test
- Check "Connection View"
- Look for pushed resources
What to Look For:
- Resources marked as "PUSH_PROMISE"
- Pushed resources vs. requested resources
- Push timing and efficiency
Method 4: Command Line Testing
# Check for HTTP/2 support
curl -I --http2 https://example.com
# Look for:
HTTP/2 200
# Check for Link headers
curl -I https://example.com | grep -i "link:"
Method 5: Server Configuration Check
Apache:
# Check if mod_http2 is enabled
apache2ctl -M | grep http2
# Check configuration
grep -r "H2Push" /etc/apache2/
Nginx:
# Check nginx version (1.13.9+ for push)
nginx -v
# Check configuration
grep -r "http2_push" /etc/nginx/
General Fixes
Fix 1: Migrate to Preload (Recommended)
Replace server push with <link rel="preload">:
<!-- In HTML <head> -->
<link rel="preload" href="/css/critical.css" as="style">
<link rel="preload" href="/js/app.js" as="script">
<link rel="preload" href="/fonts/font.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/images/hero.jpg" as="image">
<!-- Then load normally -->
<link rel="stylesheet" href="/css/critical.css">
<script src="/js/app.js" defer></script>
Benefits:
- Better browser support
- Browser handles caching properly
- Easier to implement
- More granular control
Fix 2: Implement 103 Early Hints
Modern alternative to server push:
Server sends 103 response immediately:
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
...
Cloudflare Example:
// Cloudflare Worker
export default {
async fetch(request) {
const response = await fetch(request);
// Send Early Hints
return new Response(response.body, {
status: response.status,
headers: {
...response.headers,
'Link': '</css/style.css>; rel=preload; as=style, </js/app.js>; rel=preload; as=script'
}
});
}
}
Fix 3: Remove Server Push Configuration
Apache (.htaccess or httpd.conf):
# Remove these directives:
# H2Push Off
# H2PushResource add /css/style.css
# H2PushPriority * after
# Or explicitly disable:
H2Push Off
Nginx (nginx.conf):
# Remove these directives:
# http2_push /css/style.css;
# http2_push /js/app.js;
# Comment them out:
# location / {
# http2_push /css/style.css;
# }
Node.js (Express):
// Remove server push code
// const http2 = require('http2');
// stream.pushStream({ ':path': '/css/style.css' }, ...);
// Use Link headers for preload instead
app.use((req, res, next) => {
res.setHeader('Link', '</css/style.css>; rel=preload; as=style');
next();
});
Fix 4: Optimize Preload Strategy
Only preload critical resources:
<!-- Good: Preload only critical above-fold resources -->
<head>
<!-- Critical CSS -->
<link rel="preload" href="/css/critical.css" as="style">
<!-- Critical font -->
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
<!-- LCP image -->
<link rel="preload" href="/images/hero.jpg" as="image" fetchpriority="high">
<!-- Critical JavaScript -->
<link rel="preload" href="/js/app.js" as="script">
</head>
<!-- Bad: Preloading everything -->
<head>
<link rel="preload" href="/css/style.css" as="style">
<link rel="preload" href="/css/responsive.css" as="style">
<link rel="preload" href="/css/print.css" as="style">
<link rel="preload" href="/js/analytics.js" as="script">
<link rel="preload" href="/js/ads.js" as="script">
<!-- Too many preloads harm performance -->
</head>
Fix 5: Use Resource Hints Properly
Combine preload with other hints:
<!-- DNS prefetch for third-party domains -->
<link rel="dns-prefetch" href="https://analytics.google.com">
<!-- Preconnect for critical third-party resources -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Preload critical resources -->
<link rel="preload" href="/css/critical.css" as="style">
<!-- Prefetch for next page navigation -->
<link rel="prefetch" href="/next-page.html">
Fix 6: Implement Priority Hints
Use fetchpriority for better control:
<!-- High priority: LCP image -->
<img src="hero.jpg" fetchpriority="high" alt="Hero">
<!-- Low priority: Below-fold image -->
<img src="footer-logo.png" fetchpriority="low" loading="lazy" alt="Logo">
<!-- High priority: Critical CSS -->
<link rel="preload" href="/critical.css" as="style" fetchpriority="high">
<!-- Low priority: Analytics -->
<script src="/analytics.js" fetchpriority="low" async></script>
Fix 7: Configure CDN-Level Optimizations
Cloudflare:
- Enable "Early Hints" in Speed settings
- Configure custom cache rules
- Use Workers for dynamic hints
Other CDNs:
- Check for Early Hints support
- Configure edge caching properly
- Review resource prioritization options
Platform-Specific Guides
Detailed implementation instructions for your specific platform:
| Platform | Troubleshooting Guide |
|---|---|
| Apache | Apache Preload Configuration |
| Nginx | Nginx Resource Hints |
| Cloudflare | Cloudflare Early Hints |
| WordPress | WordPress Resource Hints |
| Node.js | Node.js Preload Setup |
Testing & Validation
After implementing fixes:
Step 1: Verify Preload Headers
# Check for Link headers
curl -I https://example.com
# Look for:
Link: </css/style.css>; rel=preload; as=style
Step 2: Chrome DevTools Network Tab
- Open DevTools (
F12) - Go to Network tab
- Enable "Priority" column
- Reload page
- Verify:
- Preloaded resources load early
- Correct priorities (High, Low, etc.)
- No duplicate requests
Step 3: PageSpeed Insights
- Run test on PageSpeed Insights
- Check for warnings:
- "Preload key requests" - should pass if implemented
- No warnings about render-blocking
- Verify improved FCP and LCP
Step 4: WebPageTest
- Run test on WebPageTest
- Check waterfall chart
- Verify preloaded resources start early
- Check for connection efficiency
Step 5: Lighthouse Audit
- Open DevTools
- Go to Lighthouse tab
- Run Performance audit
- Check:
- "Preload key requests" passes
- "Uses passive listeners" passes
- Overall performance score improved
Common Mistakes
- Keeping old server push config - Remove deprecated configurations
- Preloading too many resources - Limit to 3-5 critical resources
- Preloading without using - Every preload must be used on the page
- Wrong as= attribute - Must match resource type
- Forgetting crossorigin for fonts - Required for CORS
- Not testing on slow connections - Over-preloading hurts slow connections
- Preloading third-party resources - Only preload same-origin or CORS-enabled
- Missing fetchpriority - Not utilizing modern priority hints
Migration Checklist
Moving from Server Push to modern alternatives:
Audit Current Setup
- Document all pushed resources
- Identify critical vs. non-critical resources
- Check browser support requirements
Implementation
- Remove server push configuration
- Add preload links for critical resources
- Implement priority hints
- Configure 103 Early Hints (if CDN supports)
Testing
- Test with Chrome DevTools
- Run PageSpeed Insights
- Test on slow connections
- Verify performance metrics improved
Monitoring
- Monitor Core Web Vitals
- Track LCP and FCP metrics
- Watch for regressions
- Adjust preload strategy as needed