View contents
Complete Guide to HTTP/2 and HTTP/3 Migration for Web Performance
Introduction
Migrating to HTTP/2 and HTTP/3 is one of the highest-impact performance improvements you can make. Modern protocols offer significant speed improvements with relatively straightforward implementation. This guide provides comprehensive migration strategies for different server environments.
New to HTTP/2 and HTTP/3? Start with our beginner-friendly guide: 📖 Read: HTTP/2 & HTTP/3 Explained
Strategy 1: Nginx HTTP/2 and HTTP/3 Configuration
HTTP/2 Setup
# /etc/nginx/sites-available/default
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com;
# SSL Certificate (required for HTTP/2)
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Modern TLS configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# HTTP/2 specific settings
http2_push_preload on; # Enable Link preload push
http2_max_concurrent_streams 128;
http2_idle_timeout 3m;
root /var/www/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
# Redirect HTTP to HTTPS
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
HTTP/3 Setup (Nginx 1.25+)
# /etc/nginx/nginx.conf
http {
# Enable HTTP/3 support
http3 on;
http3_hq on;
server {
# HTTP/3 over QUIC
listen 443 quic reuseport;
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# TLS 1.3 required for HTTP/3
ssl_protocols TLSv1.3;
ssl_early_data on; # Enable 0-RTT
# Advertise HTTP/3 support
add_header Alt-Svc 'h3=":443"; ma=86400';
# QUIC-specific settings
quic_retry on;
quic_gso on;
location / {
try_files $uri $uri/ =404;
}
}
}
Strategy 2: Apache HTTP/2 Configuration
Enable HTTP/2 Module
# /etc/apache2/mods-available/http2.load
LoadModule http2_module /usr/lib/apache2/modules/mod_http2.so
# Enable the module
# a2enmod http2
Virtual Host Configuration
# /etc/apache2/sites-available/example.conf
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
# Enable HTTP/2
Protocols h2 h2c http/1.1
# SSL Configuration (required)
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
# Modern TLS settings
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
SSLHonorCipherOrder off
# HTTP/2 settings
H2Direct on
H2ModernTLSOnly off
H2MaxSessionStreams 100
H2WindowSize 65535
DocumentRoot /var/www/html
<Directory /var/www/html>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
# HTTP to HTTPS redirect
<VirtualHost *:80>
ServerName example.com
Redirect permanent / https://example.com/
</VirtualHost>
Strategy 3: CDN-Based HTTP/2 and HTTP/3
Cloudflare Configuration
Cloudflare Dashboard → Speed → Optimization → Protocol Optimization
✅ HTTP/2: Enabled by default (automatic)
✅ HTTP/3: Enable in dashboard
Settings:
- HTTP/2 to Origin: Enable (if origin supports)
- HTTP/3: Toggle ON
- 0-RTT Connection Resumption: Enable
AWS CloudFront
{
"DistributionConfig": {
"HttpVersion": "http2and3",
"Origins": {
"Items": [{
"OriginProtocolPolicy": "https-only",
"CustomOriginConfig": {
"OriginProtocolPolicy": "https-only",
"HTTPSPort": 443,
"OriginSSLProtocols": {
"Items": ["TLSv1.2"],
"Quantity": 1
}
}
}]
},
"DefaultCacheBehavior": {
"ViewerProtocolPolicy": "redirect-to-https",
"AllowedMethods": ["GET", "HEAD", "OPTIONS"]
}
}
}
Vercel and Netlify
Both platforms automatically provide HTTP/2 and HTTP/3:
Vercel:
- HTTP/2: Automatic ✅
- HTTP/3: Automatic ✅
- No configuration needed
Netlify:
- HTTP/2: Automatic ✅
- HTTP/3: Automatic ✅
- No configuration needed
Strategy 4: Node.js HTTP/2 Server
Native HTTP/2 Implementation
// server.js
const http2 = require('http2');
const fs = require('fs');
const path = require('path');
const server = http2.createSecureServer({
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.crt'),
allowHTTP1: true // Fallback for HTTP/1.1 clients
});
// Handle streams
server.on('stream', (stream, headers) => {
const reqPath = headers[':path'];
// Server push example (use sparingly)
if (reqPath === '/' && stream.pushAllowed) {
stream.pushStream({ ':path': '/styles.css' }, (err, pushStream) => {
if (!err) {
pushStream.respondWithFile(
path.join(__dirname, 'public', 'styles.css'),
{ 'content-type': 'text/css' }
);
}
});
}
// Respond to request
const filePath = reqPath === '/' ? '/index.html' : reqPath;
stream.respondWithFile(
path.join(__dirname, 'public', filePath),
{ 'content-type': getContentType(filePath) },
{ onError: (err) => handleError(stream, err) }
);
});
server.listen(443, () => {
console.log('HTTP/2 server running on port 443');
});
Express with HTTP/2
// Using spdy package for HTTP/2 with Express
const spdy = require('spdy');
const express = require('express');
const fs = require('fs');
const app = express();
// Middleware and routes
app.use(express.static('public'));
app.get('/', (req, res) => {
// Server push via Link header
res.setHeader('Link', '</styles.css>; rel=preload; as=style');
res.sendFile(__dirname + '/public/index.html');
});
const options = {
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.crt'),
spdy: {
protocols: ['h2', 'http/1.1'],
plain: false
}
};
spdy.createServer(options, app).listen(443, () => {
console.log('HTTP/2 server with Express running');
});
Strategy 5: Optimization Best Practices
Remove HTTP/1.1 Workarounds
After HTTP/2 Migration - What to Change:
┌─────────────────────────────────────────────────────────────┐
│ REMOVE These HTTP/1.1 Workarounds: │
│ │
│ ❌ Domain sharding (multiple CDN domains) │
│ └── HTTP/2 multiplexes on single connection │
│ │
│ ❌ Excessive file concatenation │
│ └── Many small files now efficient │
│ │
│ ❌ Image sprites (for all images) │
│ └── Individual images can load in parallel │
│ │
│ ❌ Inlining everything (CSS, JS, images) │
│ └── External files can be cached and pushed │
│ │
│ KEEP These Practices: │
│ │
│ ✅ Minification (still reduces bytes) │
│ ✅ Compression (gzip/Brotli still essential) │
│ ✅ Caching (cache headers still critical) │
│ ✅ Code splitting (reasonable chunks, not one huge file) │
│ ✅ Critical CSS (first-render optimization) │
└─────────────────────────────────────────────────────────────┘
Preload and Early Hints
<!-- Use preload with HTTP/2 push -->
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/styles/critical.css" as="style">
<link rel="preload" href="/scripts/main.js" as="script">
<!-- Server-side Early Hints (103) -->
<!-- Nginx config -->
<!-- add_header Link "</styles.css>; rel=preload; as=style" early_hints; -->
Optimal Resource Chunking
// vite.config.js - Optimal chunking for HTTP/2
export default defineConfig({
build: {
rollupOptions: {
output: {
// Smaller chunks work better with HTTP/2
manualChunks: {
vendor: ['vue', 'vue-router', 'pinia'],
utils: ['lodash', 'axios'],
},
// Target ~50-100KB per chunk
chunkSizeWarningLimit: 100,
},
},
},
});
Strategy 6: Testing and Verification
Testing HTTP/2 Support
# Using curl
curl -I --http2 -s https://example.com | head -1
# Expected: HTTP/2 200
# Check negotiated protocol
curl -v --http2 https://example.com 2>&1 | grep "ALPN"
# Expected: ALPN, server accepted to use h2
# nghttp tool (detailed HTTP/2 analysis)
nghttp -nv https://example.com
Testing HTTP/3 Support
# Using curl with HTTP/3 (requires curl 7.66+)
curl --http3 -I https://example.com
# Check Alt-Svc header
curl -I https://example.com 2>&1 | grep -i "alt-svc"
# Expected: alt-svc: h3=":443"; ma=86400
Online Testing Tools
| Tool | Purpose | URL |
|---|---|---|
| HTTP/2 Test | Verify HTTP/2 support | https://tools.keycdn.com/http2-test |
| HTTP/3 Check | Verify HTTP/3 support | https://http3check.net |
| WebPageTest | Full protocol analysis | https://webpagetest.org |
| Lighthouse | Performance audit | Built into Chrome DevTools |
Measuring Migration Impact
Before/After Comparison
BEFORE HTTP/2 Migration (HTTP/1.1):
├── Connections per page: 6-8 parallel
├── Time to First Byte: 450ms
├── Page Load Time: 3.2s
├── Total Requests: 45
└── Lighthouse Performance: 68
AFTER HTTP/2 Migration:
├── Connections per page: 1-2 multiplexed
├── Time to First Byte: 380ms (↓15%)
├── Page Load Time: 2.4s (↓25%)
├── Total Requests: 45 (same, but faster)
└── Lighthouse Performance: 82 (↑14 points)
AFTER HTTP/3 Migration:
├── Connections per page: 1 QUIC
├── Time to First Byte: 320ms (↓29%)
├── Page Load Time: 1.9s (↓40%)
├── 0-RTT for returning visitors
└── Lighthouse Performance: 89 (↑21 points)
Migration Checklist
Before going live, verify:
- [ ] HTTPS is fully configured (required for HTTP/2)
- [ ] TLS 1.2+ enabled, TLS 1.3 for HTTP/3
- [ ] HTTP/2 enabled and working (test with curl)
- [ ] Alt-Svc header advertising HTTP/3 (if enabled)
- [ ] Domain sharding removed (use single domain)
- [ ] Excessive bundling reduced
- [ ] Server push configured sparingly (or disabled)
- [ ] Fallback to HTTP/1.1 works for old clients
- [ ] Performance metrics improved in testing
- [ ] CDN supports HTTP/2 and HTTP/3
Related Articles
Continue optimizing your server configuration:
- HTTP/2 & HTTP/3 Explained - Understand the basics
- TTFB Optimization Guide - Protocols impact TTFB directly
- Caching Headers Configuration Guide - Combine with protocol optimization
- Text Compression Guide - Essential for both protocols
📚 Back to Performance SEO Hub - Explore all performance topics
References
- MDN Web Docs - HTTP/2
- MDN Web Docs - HTTP/3
- web.dev - Introduction to HTTP/2
- Cloudflare - HTTP/3 Support
Try It Yourself
Ready to check your site’s HTTP protocol support?
🔧 Download UXR SEO Analyzer (Free, 100% local analysis)
Disclaimer: The analyzers in this extension are reference guides based on official documentation from MDN, web.dev, and Chrome Developers. They do not represent absolute truths about how search engines evaluate your content—only search engines know their internal algorithms. Use these recommendations as a starting point to improve your site.
Last updated: December 15, 2025