Introduction to Network Performance
Network performance is one of the most critical factors affecting web application speed. While you can optimize your frontend code perfectly, poor network performance can still result in slow page loads. The time it takes to download resources, establish connections, and transfer data directly impacts user experience.
In this comprehensive lesson, we'll explore modern network optimization techniques including HTTP/2 and HTTP/3 protocols, resource hints, Content Delivery Networks (CDNs), compression algorithms, caching strategies, Service Workers, and DNS optimization. These techniques can dramatically reduce load times and improve perceived performance.
Network Performance Impact: Network latency and bandwidth limitations are often the primary bottlenecks in web performance. A user on a 3G connection may wait 10+ seconds for a page that loads in 1 second on fiber optic. Optimizing network performance ensures fast experiences for all users, regardless of their connection quality.
HTTP/2 and HTTP/3 Protocols
HTTP/2 and HTTP/3 are modern protocols that significantly improve performance over HTTP/1.1 through multiplexing, header compression, and other optimizations.
HTTP/1.1 Limitations
HTTP/1.1, while revolutionary in its time, has several performance limitations:
- Head-of-Line Blocking: Only one request can be active per connection at a time
- Redundant Headers: Headers are sent with every request, wasting bandwidth
- Multiple Connections: Browsers open 6-8 parallel connections to work around blocking
- No Priority: All resources have equal priority, critical resources may wait
HTTP/2 Improvements
HTTP/2 addresses these limitations with several key features:
- Multiplexing: Multiple requests and responses can be in flight simultaneously over a single connection
- Header Compression: HPACK compression reduces redundant header data
- Server Push: Servers can proactively send resources before they're requested
- Stream Prioritization: Critical resources can be prioritized over less important ones
// Server Push example (Apache configuration)\n<FilesMatch "\.html$">\n Header add Link "</css/style.css>; rel=preload; as=style"\n Header add Link "</js/app.js>; rel=preload; as=script"\n</FilesMatch>\n\n// In Node.js with HTTP/2\nconst http2 = require('http2');\nconst fs = require('fs');\n\nconst server = http2.createSecureServer({\n key: fs.readFileSync('key.pem'),\n cert: fs.readFileSync('cert.pem')\n});\n\nserver.on('stream', (stream, headers) => {\n if (headers[':path'] === '/') {\n // Push critical CSS before HTML response\n stream.pushStream({ ':path': '/css/critical.css' }, (err, pushStream) => {\n pushStream.respondWithFile('css/critical.css');\n });\n stream.respondWithFile('index.html');\n }\n});
HTTP/3 and QUIC
HTTP/3 is the latest evolution, built on top of QUIC (Quick UDP Internet Connections) instead of TCP. Key advantages include:
- No Head-of-Line Blocking: Independent streams prevent one slow stream from blocking others
- Faster Connection Setup: 0-RTT resumption for returning visitors
- Better Mobile Performance: Connection migration when switching networks (WiFi to cellular)
- Built-in Encryption: TLS 1.3 is mandatory and integrated into QUIC
Enabling HTTP/2/3:- Requires HTTPS (TLS certificate)
- Most modern servers support HTTP/2 (nginx, Apache 2.4.17+, Caddy)
- HTTP/3 support is growing (Cloudflare, LiteSpeed, nginx 1.25+)
- Check support: Chrome DevTools Network tab shows protocol (h2, h3)
- No code changes needed - protocol negotiation is automatic
Resource Hints
Resource hints are HTML directives that help browsers make intelligent decisions about resource loading, improving perceived performance by predicting user actions.
Preconnect
Preconnect establishes early connections to important third-party origins, saving time on DNS lookup, TCP handshake, and TLS negotiation.
<!-- Establish early connection to analytics domain -->\n<link rel="preconnect" href="https://www.google-analytics.com">\n\n<!-- Preconnect to CDN with crossorigin attribute -->\n<link rel="preconnect" href="https://cdn.example.com" crossorigin>\n\n<!-- Preconnect to font provider -->\n<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
Use preconnect for 2-3 critical origins that will definitely be used. Each connection consumes memory and CPU, so don't overuse it.
DNS-Prefetch
DNS-prefetch resolves domain names in advance, which is lighter than preconnect but only handles DNS lookup.
<!-- Resolve DNS for domains used later on page -->\n<link rel="dns-prefetch" href="https://api.example.com">\n<link rel="dns-prefetch" href="https://images.example.com">\n<link rel="dns-prefetch" href="https://videos.example.com">
Preload
Preload tells the browser to fetch critical resources as soon as possible, even before the HTML parser discovers them.
<!-- Preload critical CSS -->\n<link rel="preload" href="/css/critical.css" as="style">\n\n<!-- Preload hero image -->\n<link rel="preload" href="/images/hero.jpg" as="image">\n\n<!-- Preload critical JavaScript -->\n<link rel="preload" href="/js/app.js" as="script">\n\n<!-- Preload web font to prevent FOIT -->\n<link rel="preload" href="/fonts/roboto.woff2" as="font" type="font/woff2" crossorigin>\n\n<!-- Preload video for immediate playback -->\n<link rel="preload" href="/videos/intro.mp4" as="video">
Preload Carefully: Preload adds resources to the browser's high-priority queue. Overusing it can delay other important resources. Only preload resources that are needed within the first few seconds of page load. Typical candidates: critical CSS, hero images, above-the-fold fonts.
Prefetch
Prefetch hints to the browser that a resource might be needed for future navigation, fetching it during idle time.
<!-- Prefetch next page in sequence -->\n<link rel="prefetch" href="/page2.html">\n\n<!-- Prefetch product detail assets for likely clicks -->\n<link rel="prefetch" href="/css/product-detail.css">\n<link rel="prefetch" href="/js/product-viewer.js">\n\n<!-- Prefetch based on analytics -->\n<script>\n // Prefetch most viewed products\n const popularProducts = ['/product/123', '/product/456'];\n popularProducts.forEach(url => {\n const link = document.createElement('link');\n link.rel = 'prefetch';\n link.href = url;\n document.head.appendChild(link);\n });\n</script>
Prerender
Prerender loads and renders an entire page in the background, making navigation feel instant. Use sparingly as it consumes significant resources.
<!-- Prerender next likely page -->\n<link rel="prerender" href="/checkout.html">\n\n<!-- Dynamic prerender based on hover intent -->\n<script>\n let prerenderLink = null;\n document.querySelectorAll('a').forEach(link => {\n link.addEventListener('mouseenter', () => {\n if (!prerenderLink) {\n prerenderLink = document.createElement('link');\n prerenderLink.rel = 'prerender';\n prerenderLink.href = link.href;\n document.head.appendChild(prerenderLink);\n }\n });\n });\n</script>
Content Delivery Networks (CDNs)
A CDN is a geographically distributed network of servers that cache and deliver content from locations closer to users, reducing latency and improving load times.
How CDNs Work
- User requests a resource (e.g., image, CSS file)
- DNS resolves to the nearest CDN edge server
- If the resource is cached, the edge server returns it immediately (cache hit)
- If not cached, the edge server fetches it from the origin server (cache miss)
- Edge server caches the resource for future requests
- Subsequent users get the cached version from the edge server
CDN Benefits
- Reduced Latency: Content served from geographically closer servers
- Better Availability: Content remains accessible if origin server fails
- Reduced Origin Load: CDN absorbs traffic, protecting origin servers
- DDoS Protection: CDN can absorb and filter malicious traffic
- SSL/TLS Offloading: CDN handles encryption, reducing origin server load
// Implementing CDN for static assets\n// Before: Direct origin server URLs\n<img src="https://example.com/images/photo.jpg">\n<link rel="stylesheet" href="https://example.com/css/style.css">\n\n// After: CDN URLs\n<img src="https://cdn.example.com/images/photo.jpg">\n<link rel="stylesheet" href="https://cdn.example.com/css/style.css">\n\n// Dynamic CDN URL generation\nconst CDN_URL = 'https://cdn.example.com';\nfunction getCDNUrl(path) {\n return `${CDN_URL}${path}`;\n}\n\n// Use in application\nconst imageUrl = getCDNUrl('/images/photo.jpg');\nconst cssUrl = getCDNUrl('/css/style.css');
Popular CDN Providers
- Cloudflare: Free tier available, excellent DDoS protection, global network
- AWS CloudFront: Integrates with AWS services, pay-as-you-go pricing
- Fastly: Real-time cache purging, edge computing capabilities
- Akamai: Largest CDN network, enterprise-focused
- StackPath: Affordable, good performance, edge computing
CDN Best Practices:- Use CDN for all static assets (images, CSS, JS, fonts)
- Enable HTTP/2 on CDN for multiplexing benefits
- Set appropriate cache headers (Cache-Control, ETag)
- Use versioned URLs or query strings for cache busting
- Enable gzip/brotli compression on CDN
- Monitor CDN performance and cache hit rates
Compression
Compression reduces the size of transferred data, resulting in faster downloads and lower bandwidth costs. Modern web servers and browsers support multiple compression algorithms.
Gzip Compression
Gzip has been the standard compression algorithm for decades, offering good compression ratios with fast decompression.
// Apache configuration (.htaccess)\n<IfModule mod_deflate.c>\n # Compress HTML, CSS, JavaScript, Text, XML and fonts\n AddOutputFilterByType DEFLATE application/javascript\n AddOutputFilterByType DEFLATE application/json\n AddOutputFilterByType DEFLATE application/xhtml+xml\n AddOutputFilterByType DEFLATE application/xml\n AddOutputFilterByType DEFLATE font/opentype\n AddOutputFilterByType DEFLATE font/otf\n AddOutputFilterByType DEFLATE font/ttf\n AddOutputFilterByType DEFLATE image/svg+xml\n AddOutputFilterByType DEFLATE image/x-icon\n AddOutputFilterByType DEFLATE text/css\n AddOutputFilterByType DEFLATE text/html\n AddOutputFilterByType DEFLATE text/javascript\n AddOutputFilterByType DEFLATE text/plain\n AddOutputFilterByType DEFLATE text/xml\n</IfModule>\n\n// Nginx configuration\ngzip on;\ngzip_vary on;\ngzip_proxied any;\ngzip_comp_level 6;\ngzip_types text/plain text/css text/xml text/javascript \n application/json application/javascript application/xml+rss \n application/xhtml+xml application/x-font-ttf \n application/x-web-app-manifest+json font/opentype \n image/svg+xml image/x-icon;
Brotli Compression
Brotli is a modern compression algorithm developed by Google that offers 15-25% better compression than gzip, especially for text-based resources.
// Nginx with Brotli module\nbrotli on;\nbrotli_comp_level 6;\nbrotli_types text/plain text/css application/json application/javascript \n text/xml application/xml application/xml+rss text/javascript \n image/svg+xml;\n\n// Node.js Express with compression middleware\nconst compression = require('compression');\nconst express = require('express');\nconst app = express();\n\n// Enable gzip compression\napp.use(compression());\n\n// Or enable Brotli with shrink-ray-current\nconst shrinkRay = require('shrink-ray-current');\napp.use(shrinkRay());\n\napp.get('/api/data', (req, res) => {\n res.json({ message: 'This response will be compressed' });\n});
Compression Comparison:- Gzip: Universal browser support, fast, 60-80% size reduction
- Brotli: Supported by all modern browsers, slower compression but better ratios (65-85% size reduction)
- Best Practice: Enable both - serve Brotli to supporting browsers, fallback to gzip for older browsers
- Don't Compress: Images (JPEG, PNG, WebP), videos, already compressed formats
Pre-compression
For static assets, pre-compress files at build time instead of compressing on-the-fly, saving CPU cycles.
// Build script with pre-compression\nconst zlib = require('zlib');\nconst fs = require('fs');\nconst path = require('path');\n\nfunction compressFile(filePath) {\n const content = fs.readFileSync(filePath);\n \n // Create gzip version\n const gzipped = zlib.gzipSync(content, { level: 9 });\n fs.writeFileSync(filePath + '.gz', gzipped);\n \n // Create brotli version\n const brotli = zlib.brotliCompressSync(content, {\n params: {\n [zlib.constants.BROTLI_PARAM_QUALITY]: 11\n }\n });\n fs.writeFileSync(filePath + '.br', brotli);\n}\n\n// Compress all JS and CSS files\nconst files = [\n 'dist/app.js',\n 'dist/style.css',\n 'dist/vendor.js'\n];\n\nfiles.forEach(compressFile);
Caching Strategies
Effective caching reduces server requests and speeds up repeat visits. Understanding cache headers and strategies is essential for optimal performance.
Cache-Control Header
The Cache-Control header is the primary mechanism for controlling browser and CDN caching behavior.
// Immutable assets (versioned/hashed filenames)\nCache-Control: public, max-age=31536000, immutable\n\n// Fingerprinted assets (app.abc123.js)\nCache-Control: public, max-age=31536000\n\n// HTML pages (always check for updates)\nCache-Control: no-cache\n\n// API responses (cache for 5 minutes)\nCache-Control: public, max-age=300\n\n// Private user data (browser cache only)\nCache-Control: private, max-age=3600\n\n// Never cache\nCache-Control: no-store
// Node.js Express cache headers\napp.use('/static', express.static('public', {\n maxAge: '1y',\n immutable: true\n}));\n\napp.get('/api/products', (req, res) => {\n res.set('Cache-Control', 'public, max-age=300');\n res.json(products);\n});\n\napp.get('/', (req, res) => {\n res.set('Cache-Control', 'no-cache');\n res.sendFile('index.html');\n});
ETag Validation
ETags enable conditional requests, allowing browsers to verify if cached content is still valid without downloading it again.
// Server generates ETag based on content\nETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"\n\n// Browser sends ETag on subsequent requests\nIf-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"\n\n// Server responds with 304 Not Modified if content unchanged\n\n// Node.js implementation\nconst crypto = require('crypto');\n\napp.get('/api/data', (req, res) => {\n const data = { message: 'Hello World' };\n const content = JSON.stringify(data);\n const etag = crypto.createHash('md5').update(content).digest('hex');\n \n // Check if client has current version\n if (req.headers['if-none-match'] === etag) {\n return res.status(304).end();\n }\n \n res.set('ETag', etag);\n res.set('Cache-Control', 'public, max-age=300');\n res.json(data);\n});
Service Worker Caching
Service Workers provide programmatic control over caching, enabling offline functionality and sophisticated caching strategies.
// service-worker.js\nconst CACHE_NAME = 'v1';\nconst ASSETS_TO_CACHE = [\n '/',\n '/css/style.css',\n '/js/app.js',\n '/images/logo.png'\n];\n\n// Install event - cache essential assets\nself.addEventListener('install', event => {\n event.waitUntil(\n caches.open(CACHE_NAME)\n .then(cache => cache.addAll(ASSETS_TO_CACHE))\n );\n});\n\n// Fetch event - cache-first strategy\nself.addEventListener('fetch', event => {\n event.respondWith(\n caches.match(event.request)\n .then(response => {\n // Return cached version if available\n if (response) {\n return response;\n }\n // Otherwise fetch from network\n return fetch(event.request).then(response => {\n // Cache new responses\n if (response.status === 200) {\n const responseClone = response.clone();\n caches.open(CACHE_NAME).then(cache => {\n cache.put(event.request, responseClone);\n });\n }\n return response;\n });\n })\n );\n});
Caching Strategies:- Cache-First: Check cache first, fallback to network (offline support)
- Network-First: Try network first, fallback to cache (fresh content priority)
- Stale-While-Revalidate: Serve cached content while updating in background
- Cache-Only: Only serve from cache (for offline-first apps)
- Network-Only: Always fetch from network (for real-time data)
DNS Optimization
DNS lookups add latency before any data transfer begins. Optimizing DNS can significantly improve initial connection times.
Reduce DNS Lookups
Each unique domain requires a DNS lookup. Minimize the number of different domains you use.
// Bad: Multiple domains increase DNS lookups\n<img src="https://cdn1.example.com/image1.jpg">\n<img src="https://cdn2.example.com/image2.jpg">\n<script src="https://cdn3.example.com/app.js"></script>\n<link href="https://cdn4.example.com/style.css">\n\n// Good: Consolidate to fewer domains\n<img src="https://cdn.example.com/image1.jpg">\n<img src="https://cdn.example.com/image2.jpg">\n<script src="https://cdn.example.com/app.js"></script>\n<link href="https://cdn.example.com/style.css">
Use Fast DNS Providers
Choose DNS providers with global networks and fast response times:
- Cloudflare DNS: 1.1.1.1 - Fast, privacy-focused
- Google Public DNS: 8.8.8.8 - Reliable, global presence
- AWS Route 53: Anycast network, integrates with AWS
- DNS Made Easy: Enterprise-grade, excellent performance
Configure DNS Prefetching
<!-- Enable DNS prefetching for external domains -->\n<meta http-equiv="x-dns-prefetch-control" content="on">\n\n<!-- Prefetch specific domains -->\n<link rel="dns-prefetch" href="//fonts.googleapis.com">\n<link rel="dns-prefetch" href="//www.google-analytics.com">\n<link rel="dns-prefetch" href="//cdn.jsdelivr.net">
Monitor DNS Performance
// Measure DNS lookup time\nconst timing = performance.getEntriesByType('navigation')[0];\nconst dnsTime = timing.domainLookupEnd - timing.domainLookupStart;\nconsole.log('DNS lookup took:', dnsTime, 'ms');\n\n// Monitor all DNS lookups\nconst resources = performance.getEntriesByType('resource');\nresources.forEach(resource => {\n const dns = resource.domainLookupEnd - resource.domainLookupStart;\n if (dns > 0) {\n console.log(`DNS for ${resource.name}: ${dns}ms`);\n }\n});
Practice Exercise:- Check if your server supports HTTP/2 (use Chrome DevTools Network tab)
- Add preconnect hints for 2-3 critical third-party domains on your site
- Configure gzip or brotli compression on your web server
- Implement cache headers: 1-year max-age for static assets, no-cache for HTML
- Set up a CDN (try Cloudflare free tier) for your static assets
- Measure DNS lookup times using Performance API
- Add dns-prefetch hints for external domains
- Create a Service Worker with cache-first strategy for static assets
Summary
Network performance optimization is essential for fast web applications. Modern protocols like HTTP/2 and HTTP/3 provide significant improvements through multiplexing and reduced latency. Resource hints enable browsers to predict and preload resources intelligently.
CDNs reduce latency by serving content from geographically distributed servers, while compression algorithms like gzip and brotli dramatically reduce transfer sizes. Effective caching strategies minimize redundant requests, and DNS optimization reduces initial connection times.
Service Workers provide programmatic caching control, enabling offline functionality and sophisticated performance optimizations. By implementing these network performance techniques, you can deliver fast experiences to users worldwide, regardless of their network conditions.
Key Takeaways:- Use HTTP/2 or HTTP/3 for multiplexing, header compression, and better performance
- Implement resource hints: preconnect for critical origins, preload for critical resources, prefetch for likely navigations
- Deploy static assets to a CDN for reduced latency and better availability
- Enable gzip and brotli compression for text-based resources (60-85% size reduction)
- Configure appropriate cache headers: long max-age for static assets, validation for dynamic content
- Use Service Workers for offline support and advanced caching strategies
- Optimize DNS: reduce lookups, use fast providers, implement dns-prefetch hints