CDN — Content Delivery Network: serving static assets from edge nodes
Concept
CDN (Content Delivery Network) — a network of geographically distributed servers that cache and serve content from locations closer to the user.
The problem CDNs solve: A server in Frankfurt is slow for a user in Tokyo. Network latency adds 200-300ms for every round trip across the planet. A CDN has "edge nodes" (PoPs — Points of Presence) in Tokyo, so the user's request hits a nearby server instead.
What CDNs cache:
- Static assets: Images, CSS, JavaScript, fonts. Rarely change — cache for weeks.
- API responses: Can be cached if the response is the same for all users.
- HTML: Can cache entire pages if content is not user-specific.
Cache-Control headers: Your server tells the CDN how long to cache content.
Cache-Control: public, max-age=31536000— cache for 1 year (for immutable assets like versioned JS bundles).Cache-Control: no-cache— always check with origin before serving.Cache-Control: private— browser can cache but CDN should not (user-specific responses).
CDN invalidation: When you deploy new code, old CSS/JS files are cached on CDNs. Two solutions:
- Cache busting: Include a hash in the filename (
app.a3f9b2.css). New deploy = new hash = new URL = CDN fetches fresh. Laravel Mix/Vite does this automatically. - Manual invalidation: Tell the CDN to delete cached files (CDN API call, slower).
Popular CDNs: Cloudflare (also provides security/DDoS protection), AWS CloudFront, Fastly, Bunny CDN.
Full-site CDN: Cloudflare can sit in front of your entire domain — all requests go through Cloudflare's edge, which caches what it can and proxies the rest to your origin.
Code Example
<?php
// CACHE-CONTROL headers in Laravel responses
Route::get('/api/products', function () {
$products = cache()->remember('products', 3600, fn() => Product::all());
return response()
->json($products)
->header('Cache-Control', 'public, max-age=3600'); // CDN caches for 1 hour
});
// Different cache for authenticated vs public
Route::get('/api/dashboard', function () {
return response()
->json(auth()->user()->dashboardData())
->header('Cache-Control', 'private, no-store'); // CDN must NOT cache this
});
// Static assets — versioned URLs (Vite handles this)
// resources/js/app.js → public/build/assets/app-a3f9b2c1.js
// The hash changes on every build → CDN automatically fetches the new version
// Old version is still valid at old URL (no invalidation needed)