Performance

Caching e CDN

Estratégias de cache e uso de CDN para melhorar performance.

Por que Caching e CDN?

Cache e CDN são fundamentais para performance:

  • Reduz latência: Conteúdo servido mais próximo do usuário
  • Reduz carga no servidor: Menos requisições ao servidor original
  • Melhora experiência: Carregamento mais rápido
  • Reduz custos: Menos banda e processamento

Tipos de Cache

1. Browser Cache (Cache do Navegador)

Cache local no dispositivo do usuário.

Cache-Control Headers

# Cache por 1 ano (recursos estáticos)
Cache-Control: public, max-age=31536000, immutable

# Cache por 1 hora (conteúdo dinâmico)
Cache-Control: public, max-age=3600

# Sem cache (conteúdo sempre atualizado)
Cache-Control: no-cache, no-store, must-revalidate

ETag e Last-Modified

# ETag - hash do conteúdo
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"

# Last-Modified - data de modificação
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT

2. Service Worker Cache

Cache programático no navegador.

// Service Worker - Cache First
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((response) => {
      // Retorna do cache se disponível
      if (response) {
        return response;
      }
      // Senão, busca da rede
      return fetch(event.request).then((response) => {
        // Adiciona ao cache
        const responseClone = response.clone();
        caches.open('v1').then((cache) => {
          cache.put(event.request, responseClone);
        });
        return response;
      });
    })
  );
});

Estratégias de Cache

// Cache First (para recursos estáticos)
async function cacheFirst(request) {
  const cached = await caches.match(request);
  if (cached) return cached;
  const response = await fetch(request);
  const cache = await caches.open('v1');
  cache.put(request, response.clone());
  return response;
}

// Network First (para conteúdo dinâmico)
async function networkFirst(request) {
  try {
    const response = await fetch(request);
    const cache = await caches.open('v1');
    cache.put(request, response.clone());
    return response;
  } catch (error) {
    return await caches.match(request);
  }
}

// Stale While Revalidate
async function staleWhileRevalidate(request) {
  const cache = await caches.open('v1');
  const cached = await cache.match(request);
  const fetchPromise = fetch(request).then((response) => {
    cache.put(request, response.clone());
    return response;
  });
  return cached || fetchPromise;
}

3. HTTP Cache

Cache intermediário (proxies, CDN).

# Cache público compartilhado
Cache-Control: public, max-age=3600

# Cache privado (apenas navegador)
Cache-Control: private, max-age=3600

# Revalidação obrigatória
Cache-Control: must-revalidate

Content Delivery Network (CDN)

O que é CDN?

Rede de servidores distribuídos geograficamente que servem conteúdo estático.

Benefícios

  • Latência reduzida: Conteúdo mais próximo do usuário
  • Alta disponibilidade: Redundância de servidores
  • Redução de carga: Menos requisições ao servidor original
  • Otimizações: Compressão, minificação automática

Quando Usar CDN

  • Recursos estáticos: CSS, JS, imagens, fontes
  • Conteúdo global: Sites com audiência internacional
  • Alto tráfego: Sites com muitas requisições
  • Conteúdo grande: Vídeos, imagens grandes

Configuração Básica

<!-- Recursos estáticos via CDN -->
<link rel="stylesheet" href="https://cdn.example.com/css/app.css">
<script src="https://cdn.example.com/js/app.js"></script>
<img src="https://cdn.example.com/images/hero.jpg" alt="Hero">

Estratégias de Cache por Tipo

HTML

# HTML - revalidação frequente
Cache-Control: no-cache, must-revalidate

CSS e JavaScript

# CSS/JS - cache longo com versionamento
Cache-Control: public, max-age=31536000, immutable

# Com hash no nome do arquivo
app.a1b2c3d4.js

Imagens

# Imagens - cache longo
Cache-Control: public, max-age=31536000

# Com versionamento ou hash
image-v2.jpg
image.abc123.jpg

APIs

# APIs - cache curto com revalidação
Cache-Control: private, max-age=300, must-revalidate

Versionamento de Recursos

Hash no Nome do Arquivo

// Build gera arquivos com hash
app.a1b2c3d4.js
styles.b5c6d7e8.css
<!-- HTML referencia arquivos versionados -->
<script src="/js/app.a1b2c3d4.js"></script>
<link rel="stylesheet" href="/css/styles.b5c6d7e8.css">

Query String

<!-- Menos ideal, mas funcional -->
<script src="/js/app.js?v=1.2.3"></script>
<link rel="stylesheet" href="/css/styles.css?v=1.2.3">

Cache Invalidation

Estratégias

  1. Versionamento: Hash no nome do arquivo
  2. Query string: Parâmetro de versão
  3. Service Worker: Atualização programática
  4. Cache busting: Headers que forçam atualização

Service Worker Update

// Detectar atualização do Service Worker
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open('v2').then((cache) => {
      return cache.addAll([
        '/',
        '/css/app.css',
        '/js/app.js'
      ]);
    })
  );
  self.skipWaiting(); // Ativa imediatamente
});

self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames.map((cacheName) => {
          if (cacheName !== 'v2') {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

Headers de Cache Completos

Exemplo para Recursos Estáticos

HTTP/1.1 200 OK
Content-Type: text/css
Cache-Control: public, max-age=31536000, immutable
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT

Exemplo para HTML

HTTP/1.1 200 OK
Content-Type: text/html
Cache-Control: no-cache, must-revalidate
ETag: "abc123"
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT

CDN Providers

Principais Opções

  • Cloudflare: CDN gratuito com otimizações
  • AWS CloudFront: Integração com AWS
  • Google Cloud CDN: Integração com GCP
  • Fastly: CDN de alta performance
  • Vercel/Netlify: CDN integrado com deploy

Configuração Cloudflare

// Cloudflare Workers - edge computing
addEventListener('fetch', (event) => {
  event.respondWith(handleRequest(event.request));
});

async function handleRequest(request) {
  // Lógica no edge
  const response = await fetch(request);
  return response;
}

Checklist de Cache

  • Cache-Control headers configurados
  • ETag ou Last-Modified implementados
  • Versionamento de recursos estáticos
  • Service Worker para cache offline
  • CDN configurado para recursos estáticos
  • Cache invalidation strategy definida
  • Testes de cache em diferentes cenários

Exemplo Completo

// Service Worker completo
const CACHE_NAME = 'app-v1';
const urlsToCache = [
  '/',
  '/css/app.css',
  '/js/app.js',
  '/images/logo.png'
];

// Install - cache inicial
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => {
      return cache.addAll(urlsToCache);
    })
  );
});

// Activate - limpar caches antigos
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames.map((cacheName) => {
          if (cacheName !== CACHE_NAME) {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

// Fetch - estratégia de cache
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((response) => {
      // Cache hit
      if (response) {
        return response;
      }
      // Cache miss - buscar da rede
      return fetch(event.request).then((response) => {
        // Verificar se resposta é válida
        if (!response || response.status !== 200) {
          return response;
        }
        // Clonar resposta para cache
        const responseToCache = response.clone();
        caches.open(CACHE_NAME).then((cache) => {
          cache.put(event.request, responseToCache);
        });
        return response;
      });
    })
  );
});
Teste seu cache em diferentes cenários: primeira visita, visitas subsequentes, e após atualizações. Use DevTools para verificar headers de cache.