📁 Volume III: Advanced Architecture

🌍 Topic 14: CDN & Edge Caching

Keep Laravel away from static files. Serve from the edge.

"Your CSS, JS, and images don't need Laravel.
They don't need PHP. They don't need sessions or middleware.
But every time you serve them through Laravel, you waste CPU, memory, and bandwidth.
Move them to a CDN. Serve them from the edge. Your application server will thank you."
⚠️ THE STATIC ASSET SIN

Many Laravel apps serve CSS, JS, and images through Laravel itself (even if they're in public/, Laravel still boots on every request). A single page load might request 50 static assets. That's 50 unnecessary Laravel bootstraps per page view. With 10,000 visitors, that's 500,000 wasted Laravel requests daily.

🔴 The Problem: Laravel Serving Static Files

STATIC FILES SERVED BY LARAVEL (BAD) ═══════════════════════════════════════════════════════════════════ User's Browser │ │ 1. HTML Page (Laravel) ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Browser parses HTML, finds: │ │ <link href="/css/app.css"> │ │ <script src="/js/app.js"> │ │ <img src="/images/logo.png"> │ └─────────────────────────────────────────────────────────────────┘ │ │ 2. Request for /css/app.css ▼ ┌─────────────────────────────────────────────────────────────────┐ │ LARAVEL APPLICATION │ │ │ │ • Boot Laravel (load config, routes, providers) │ │ • Run middleware (session, csrf, auth) │ │ • Route the request (find matching route) │ │ • Serve file from public/css/app.css │ │ │ │ COST: 20-50ms PER static file! │ └─────────────────────────────────────────────────────────────────┘ RESULTS: ┌─────────────────────────────────────────────────────────────────┐ │ • Each image/CSS/JS triggers full Laravel bootstrapping │ │ • 50 assets = 50 Laravel boots = 1-2 seconds of overhead │ │ • Wasted PHP-FPM processes (can't handle real requests) │ │ • User perceives slow page loads │ └─────────────────────────────────────────────────────────────────┘
THE STATIC SINS (What NOT to do)

✅ The Solution: Nginx + CDN + Edge Caching

STATIC FILES SERVED BY NGINX + CDN (GOOD) ═══════════════════════════════════════════════════════════════════ CDN Edge Locations ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ US East │ │ EU West │ │ Asia │ │ Australia │ │ (Virginia) │ │ (Ireland) │ │ (Tokyo) │ │ (Sydney) │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ │ └────────────────┼────────────────┼────────────────┘ │ │ ▼ ▼ ┌─────────────────────────────────┐ │ ORIGIN SERVER │ │ (Nginx + Laravel) │ │ │ │ Nginx handles static files: │ │ location ~* \.(css|js|png) { │ │ expires 1y; │ │ add_header Cache-Control; │ │ } │ │ │ │ Laravel handles dynamic: │ │ location / { │ │ try_files $uri /index.php; │ │ } │ └─────────────────────────────────┘ BENEFITS: ┌─────────────────────────────────────────────────────────────────┐ │ • Nginx serves static files in 1ms (Laravel takes 50ms) │ │ • CDN caches assets globally (50ms → 5ms latency) │ │ • Browser caches assets for 1 year (0 requests to your server)│ │ • Laravel never sees static asset requests │ │ • PHP-FPM freed for dynamic requests │ └─────────────────────────────────────────────────────────────────┘
THE GOLDEN RULE OF STATIC ASSETS

Your application server should NEVER serve static files. Nginx serves them locally. CDN serves them globally. Browser caches them permanently.

⚙️ Nginx Configuration (First Line of Defense)

NGINX STATIC FILE RULES
# /etc/nginx/sites-available/example.com

server {
    listen 80;
    server_name example.com;
    root /var/www/example.com/public;
    index index.php;

    # Static files - served directly by Nginx (no Laravel)
    location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
        log_not_found off;
        try_files $uri =404;
    }

    # HTML files (rare) - also serve directly
    location ~* \.html$ {
        expires 1h;
        add_header Cache-Control "public";
    }

    # Dynamic requests - send to Laravel
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    # PHP processing
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}
WHAT CHANGES

🚀 CDN Integration: Global Edge Caching

👎 WITHOUT CDN

User in Tokyo → Request → US Server (150ms latency) User in London → Request → US Server (80ms latency) User in Sydney → Request → US Server (200ms latency) Every request travels across the ocean.

👍 WITH CDN (CloudFront / CloudFlare)

User in Tokyo → Request → Tokyo Edge (5ms) → Cache hit! User in London → Request → London Edge (5ms) → Cache hit! User in Sydney → Request → Sydney Edge (5ms) → Cache hit! Assets are cached at 200+ edge locations worldwide.

Step-by-Step CDN Setup (CloudFlare - Free)

# 1. Change your DNS to CloudFlare
# 2. Enable "Proxy" (orange cloud) for your domain
# 3. Configure caching rules:

# CloudFlare Page Rules:
# URL: example.com/css/*
# Cache Level: Cache Everything
# Edge Cache TTL: 1 month

# URL: example.com/js/*
# Cache Level: Cache Everything
# Edge Cache TTL: 1 month

# URL: example.com/images/*
# Cache Level: Cache Everything
# Edge Cache TTL: 1 year

# 4. Configure Laravel to use CDN for assets
// config/app.php
'asset_url' => env('ASSET_URL', 'https://cdn.example.com'),

// .env
ASSET_URL=https://cdn.example.com

// In Blade (automatically prefixes CDN)
<link href="{{ asset('css/app.css') }}">
// Becomes: https://cdn.example.com/css/app.css
AWS CloudFront (with S3)
# For user-uploaded images (S3 + CloudFront)
# .env
AWS_ACCESS_KEY_ID=xxx
AWS_SECRET_ACCESS_KEY=xxx
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=myapp-uploads
CLOUDFRONT_URL=https://d123.cloudfront.net

# config/filesystems.php
'disks' => [
    's3' => [
        'driver' => 's3',
        'key' => env('AWS_ACCESS_KEY_ID'),
        'secret' => env('AWS_SECRET_ACCESS_KEY'),
        'region' => env('AWS_DEFAULT_REGION'),
        'bucket' => env('AWS_BUCKET'),
        'url' => env('CLOUDFRONT_URL'),
    ],
],

🔖 Laravel Asset Versioning (Cache Busting)

THE CACHE INVALIDATION PROBLEM

You set expires 1y — great! But now when you update app.css, users still see the old version for up to 1 year.

SOLUTION: Versioned Assets (Laravel Mix / Vite)
# webpack.mix.js (Laravel Mix)
mix.js('resources/js/app.js', 'public/js')
   .sass('resources/sass/app.scss', 'public/css')
   .version();

// Result:
// public/css/app.css?id=8a4b9c2d
// public/js/app.js?id=3e5f7a1b

// In Blade:
<link href="{{ mix('css/app.css') }}">
// Outputs: <link href="/css/app.css?id=8a4b9c2d">

// With CDN:
<link href="{{ asset(mix('css/app.css')) }}">
// Outputs: https://cdn.example.com/css/app.css?id=8a4b9c2d
VITE (Laravel 9+)
# vite.config.js
export default defineConfig({
    plugins: [laravel({
        input: ['resources/css/app.css', 'resources/js/app.js'],
        refresh: true,
    })],
});

// Vite automatically adds version hashes

// In Blade:
@vite(['resources/css/app.css', 'resources/js/app.js'])
// Outputs with version hashes automatically

📦 Browser Caching Strategy (Cache-Control Headers)

Asset Type Cache-Control Expires Why
CSS, JS (versioned) public, immutable 1 year Content never changes (new version = new filename)
Images, logos, icons public 1 year Changes rarely
User-uploaded images public 1 month Can change if user updates profile
HTML pages (dynamic) no-cache 0 Always need fresh content
API responses no-cache 0 Must be fresh
# Complete Nginx cache configuration
location ~* \.(css|js)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
    access_log off;
}

location ~* \.(jpg|jpeg|png|gif|ico|svg|webp)$ {
    expires 1y;
    add_header Cache-Control "public";
    access_log off;
}

location ~* \.(woff|woff2|ttf|eot)$ {
    expires 1y;
    add_header Cache-Control "public";
    access_log off;
}

location / {
    # Dynamic content - no cache
    add_header Cache-Control "no-cache, private";
    try_files $uri $uri/ /index.php?$query_string;
}

📊 Performance Gains: CDN + Edge Caching

Scenario Without CDN With CDN (CloudFlare/CloudFront) Improvement
User in Tokyo loading CSS 150ms (round-trip to US) 5ms (Tokyo edge) 30x faster
User in London loading JS 80ms (to US) 5ms (London edge) 16x faster
User in Sydney loading image 200ms (to US) 5ms (Sydney edge) 40x faster
Laravel CPU usage for static assets 50ms per asset (50 assets = 2.5s CPU) 0ms (Nginx/CDN) 100% reduction
Bandwidth cost (100GB/month) $9 (AWS data transfer) $0 (CloudFlare free tier) Free (or cheaper)
Overall Page Load Time 2.5 seconds 0.5 seconds 80% faster
THE BOTTOM LINE

CDN + Nginx static serving + browser caching can reduce page load time by 70-90% and reduce server load by 50-80%. It's the highest ROI performance optimization you can make.

📝 Topic 14 Summary: CDN & Edge Caching

Layer Responsibility Cache Duration Latency
Browser Cache User's device 1 year (versioned assets) 0ms (local)
CDN Edge 200+ global locations 1 month - 1 year 1-10ms
Nginx (Origin) Application server Static files only 1ms
Laravel NEVER for static assets Dynamic content only 50-200ms
📌 THE RULE: Nginx serves static files. CDN caches them globally. Browser caches them locally. Laravel NEVER sees static assets. Your response time drops from 500ms to 50ms. Your server handles 10x more traffic.

CDN and edge caching are not optional for production applications. They are table stakes.
NEXT TOPIC PREVIEW

Topic 15: The 10 Disasters (Complete List) — The most common production failures. DB::enableQueryLog() in production. env() after config:cache. File sessions. Missing indexes. Full model serialization. And how to prevent each one.