Guía detallada

Caching Headers Optimization Guide

Ver contenido

Guía Completa de Configuración de Headers de Caché HTTP para Rendimiento Web

Introducción

El caché HTTP es una de las optimizaciones de rendimiento más poderosas disponibles. Un caché correctamente configurado puede eliminar solicitudes de red por completo, reduciendo los tiempos de carga de página de segundos a milisegundos para visitantes recurrentes. Esta guía proporciona estrategias completas de implementación para diferentes entornos de servidor.

¿Nuevo en caché? Comienza con nuestra guía para principiantes: 📖 Leer: Headers de Caché Explicado

Estrategia 1: Directivas Cache-Control en Profundidad

Referencia Completa de Directivas

Directivas Cache-Control:
┌─────────────────────────────────────────────────────────────┐
│ DIRECTIVAS DE FRESCURA                                      │
│ ├── max-age=<segundos>     Cuánto tiempo el caché es fresco │
│ ├── s-maxage=<segundos>    Duración de caché CDN/proxy      │
│ └── stale-while-revalidate=<segundos>  Servir obsoleto      │
│                            mientras obtiene copia fresca    │
│                                                             │
│ DIRECTIVAS DE REVALIDACIÓN                                  │
│ ├── no-cache               Siempre revalidar antes de usar  │
│ ├── must-revalidate        Revalidar después de max-age     │
│ └── proxy-revalidate       Proxies deben revalidar          │
│                                                             │
│ DIRECTIVAS DE ALMACENAMIENTO                                │
│ ├── no-store               Nunca cachear (datos sensibles)  │
│ ├── private                Solo el navegador puede cachear  │
│ └── public                 CDNs y proxies pueden cachear    │
│                                                             │
│ DIRECTIVAS DE OPTIMIZACIÓN                                  │
│ └── immutable              Nunca revalidar (assets          │
│                            versionados)                     │
└─────────────────────────────────────────────────────────────┘

Combinando Directivas

# Assets estáticos versionados (CSS, JS con hash en nombre)
Cache-Control: public, max-age=31536000, immutable

# Páginas HTML (siempre verificar actualizaciones)
Cache-Control: no-cache

# Datos sensibles de usuario (nunca cachear)
Cache-Control: no-store, private

# Respuestas de API (caché corto con refresh en background)
Cache-Control: public, max-age=60, stale-while-revalidate=600

# Caché más largo específico para CDN
Cache-Control: public, max-age=3600, s-maxage=86400

Estrategia 2: Configuración de Servidor

Configuración de Nginx

# /etc/nginx/conf.d/caching.conf

# Assets estáticos versionados - cachear para siempre
location ~* \.(css|js)$ {
    # Solo si el nombre contiene hash (ej. app.abc123.js)
    if ($uri ~* ".*\.[a-f0-9]{8,}\.") {
        add_header Cache-Control "public, max-age=31536000, immutable";
    }
    # Respaldo para no versionados
    if ($uri !~* ".*\.[a-f0-9]{8,}\.") {
        add_header Cache-Control "public, max-age=86400";
    }
    add_header Vary "Accept-Encoding";
}

# Imágenes y fuentes - caché largo
location ~* \.(jpg|jpeg|png|gif|webp|avif|svg|ico|woff|woff2|ttf|eot)$ {
    add_header Cache-Control "public, max-age=31536000";
    add_header Vary "Accept-Encoding";
}

# HTML - siempre revalidar
location ~* \.html$ {
    add_header Cache-Control "no-cache";
    add_header Vary "Accept-Encoding";
}

# Endpoints de API - caché corto con stale-while-revalidate
location /api/ {
    add_header Cache-Control "public, max-age=60, stale-while-revalidate=600";
    add_header Vary "Accept-Encoding, Authorization";
}

# Habilitar ETag
etag on;

Configuración de Apache

# .htaccess o httpd.conf

<IfModule mod_expires.c>
    ExpiresActive On

    # Por defecto: 1 mes
    ExpiresDefault "access plus 1 month"

    # HTML: siempre revalidar
    ExpiresByType text/html "access plus 0 seconds"

    # CSS/JS: 1 año (usar nombres de archivo versionados)
    ExpiresByType text/css "access plus 1 year"
    ExpiresByType application/javascript "access plus 1 year"

    # Imágenes: 1 año
    ExpiresByType image/jpeg "access plus 1 year"
    ExpiresByType image/png "access plus 1 year"
    ExpiresByType image/gif "access plus 1 year"
    ExpiresByType image/webp "access plus 1 year"
    ExpiresByType image/svg+xml "access plus 1 year"

    # Fuentes: 1 año
    ExpiresByType font/woff "access plus 1 year"
    ExpiresByType font/woff2 "access plus 1 year"
</IfModule>

<IfModule mod_headers.c>
    # Assets versionados (archivos con hash en nombre)
    <FilesMatch "\.[a-f0-9]{8,}\.(css|js)$">
        Header set Cache-Control "public, max-age=31536000, immutable"
    </FilesMatch>

    # Archivos HTML
    <FilesMatch "\.html$">
        Header set Cache-Control "no-cache"
    </FilesMatch>

    # Habilitar ETag
    FileETag MTime Size
</IfModule>

Configuración de Node.js/Express

const express = require('express');
const path = require('path');

const app = express();

// Middleware personalizado de caché
const setCacheHeaders = (req, res, next) => {
  const url = req.url;

  // Assets versionados (contienen hash)
  if (/\.[a-f0-9]{8,}\.(css|js)$/.test(url)) {
    res.set('Cache-Control', 'public, max-age=31536000, immutable');
  }
  // Imágenes y fuentes
  else if (/\.(jpg|jpeg|png|gif|webp|svg|woff2?|ttf|eot)$/.test(url)) {
    res.set('Cache-Control', 'public, max-age=31536000');
  }
  // HTML
  else if (/\.html$/.test(url) || url === '/') {
    res.set('Cache-Control', 'no-cache');
  }
  // Respuestas de API
  else if (url.startsWith('/api/')) {
    res.set('Cache-Control', 'public, max-age=60, stale-while-revalidate=600');
  }

  next();
};

app.use(setCacheHeaders);

// Servir archivos estáticos con ETags
app.use(express.static('public', {
  etag: true,
  lastModified: true,
  maxAge: 0, // Dejar que el middleware maneje Cache-Control
}));

Estrategia 3: Versionado de Assets (Cache Busting)

Configuración de Herramientas de Build

Webpack

// webpack.config.js
module.exports = {
  output: {
    filename: '[name].[contenthash:8].js',
    chunkFilename: '[name].[contenthash:8].chunk.js',
    assetModuleFilename: 'assets/[name].[contenthash:8][ext]',
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash:8].css',
    }),
  ],
};

Vite

// vite.config.js
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        entryFileNames: 'assets/[name].[hash].js',
        chunkFileNames: 'assets/[name].[hash].js',
        assetFileNames: 'assets/[name].[hash].[ext]',
      },
    },
  },
});

Por Qué Importa el Versionado

Sin Versionado:
├── Usuario visita sitio, descarga styles.css
├── Tú actualizas styles.css
├── Usuario revisita - navegador sirve styles.css cacheado (viejo)
├── Usuario ve layout roto hasta que expira caché
└── ¡Mala experiencia de usuario!

Con Versionado:
├── Usuario visita sitio, descarga styles.abc123.css
├── Actualizas CSS, build crea styles.def456.css
├── Usuario revisita - navegador solicita styles.def456.css (¡nuevo!)
├── Usuario siempre ve estilos más recientes
└── ¡Excelente experiencia de usuario!

Estrategia 4: Configuración de Caché en CDN

Cloudflare

Page Rules:
URL: ejemplo.com/assets/*
Settings:
  - Cache Level: Cache Everything
  - Edge Cache TTL: 1 mes
  - Browser Cache TTL: 1 año

URL: ejemplo.com/*.html
Settings:
  - Cache Level: Standard
  - Edge Cache TTL: Respect Existing Headers

AWS CloudFront

{
  "CacheBehaviors": [
    {
      "PathPattern": "/assets/*",
      "DefaultTTL": 31536000,
      "MaxTTL": 31536000,
      "MinTTL": 31536000,
      "Compress": true
    },
    {
      "PathPattern": "*.html",
      "DefaultTTL": 0,
      "MaxTTL": 0,
      "MinTTL": 0
    }
  ]
}

Vercel

// vercel.json
{
  "headers": [
    {
      "source": "/assets/(.*)",
      "headers": [
        {
          "key": "Cache-Control",
          "value": "public, max-age=31536000, immutable"
        }
      ]
    },
    {
      "source": "/(.*).html",
      "headers": [
        {
          "key": "Cache-Control",
          "value": "no-cache"
        }
      ]
    }
  ]
}

Estrategia 5: Invalidación de Caché

Comparación de Estrategias

Estrategia Cómo Funciona Pros Contras
URLs versionadas Cambiar nombre de archivo Instantáneo, confiable Requiere paso de build
Query strings ?v=123 Fácil de implementar Algunos CDNs ignoran
Purge de CDN Llamada API al CDN Flexible Lento, puede fallar
TTL corto max-age bajo Simple Más solicitudes

Enfoque Recomendado

Mejor Práctica de Invalidación de Caché:
┌─────────────────────────────────────────────────────────────┐
│ Assets Estáticos (CSS, JS, Imágenes):                       │
│ └── Usar content-hash en nombre (invalidación automática)   │
│                                                             │
│ Páginas HTML:                                               │
│ └── Usar no-cache (siempre revalidar)                       │
│                                                             │
│ Respuestas de API:                                          │
│ └── Usar stale-while-revalidate para refresh en background  │
│                                                             │
│ Actualizaciones de Emergencia:                              │
│ └── API de purge del CDN + deploy nuevos assets versionados │
└─────────────────────────────────────────────────────────────┘

Estrategia 6: Header Vary para Caché Condicional

Cuándo Usar Vary

# Cachear diferentes versiones basado en encoding
Vary: Accept-Encoding

# Cachear diferentes versiones para diferentes idiomas
Vary: Accept-Language

# Cachear diferente para usuarios autenticados vs anónimos
Vary: Authorization

# Múltiples condiciones
Vary: Accept-Encoding, Accept-Language

Configuración de Vary en Nginx

# Agregar header Vary para contenido comprimido
location ~* \.(css|js|html|json|xml)$ {
    add_header Vary "Accept-Encoding";
    gzip on;
}

# Agregar Vary para contenido específico por idioma
location /content/ {
    add_header Vary "Accept-Language";
}

Midiendo la Efectividad del Caché

Comparación Antes/Después

ANTES de Optimización de Caché:
├── Carga de Página Repetida: 2.8s
├── Datos Transferidos: 1.9 MB
├── Solicitudes al Servidor: 42
├── Tasa de Acierto de Caché: 15%
└── Lighthouse Performance: 68

DESPUÉS de Optimización de Caché:
├── Carga de Página Repetida: 0.6s (↓79%)
├── Datos Transferidos: 85 KB (↓96%)
├── Solicitudes al Servidor: 8 (↓81%)
├── Tasa de Acierto de Caché: 94%
└── Lighthouse Performance: 96 (↑28 puntos)

Auditorías de Lighthouse

Verifica estos resultados:

  • “Serve static assets with an efficient cache policy” - Debería pasar
  • Muestra recursos con TTL de caché < 1 semana

Pruebas con DevTools

Análisis de Pestaña Network:
1. Habilitar checkbox "Disable cache"
2. Cargar página (simula primera visita)
3. Deshabilitar "Disable cache"
4. Recargar página (simula visita repetida)
5. Comparar columna "Size":
   - "(disk cache)" = cacheado localmente
   - "(memory cache)" = cacheado en memoria
   - Tamaño real = descargado de la red

Lista de Verificación de Optimización

Antes de desplegar, verifica:

  • [ ] Assets estáticos versionados usan max-age=31536000, immutable
  • [ ] HTML usa no-cache para contenido siempre fresco
  • [ ] Imágenes/fuentes usan max-age=31536000
  • [ ] ETags habilitados para revalidación eficiente
  • [ ] Vary: Accept-Encoding configurado para contenido comprimido
  • [ ] Caché de CDN correctamente configurado
  • [ ] Herramienta de build genera content hashes en nombres de archivo
  • [ ] Respuestas de API usan estrategia de caché apropiada
  • [ ] Datos sensibles usan no-store
  • [ ] Auditoría de caché de Lighthouse pasa

Artículos Relacionados

Continúa optimizando la entrega de tus recursos:

📚 Volver al Hub de Performance SEO - Explora todos los temas de rendimiento


Referencias

  1. MDN Web Docs - HTTP Caching
  2. web.dev - HTTP Cache
  3. Chrome Developers - Serve Static Assets with Efficient Cache Policy
  4. Google Developers - HTTP Caching

Pruébalo Tú Mismo

¿Listo para optimizar tus headers de caché?

🔧 Descarga UXR SEO Analyzer (Gratis, análisis 100% local)


Disclaimer: Los analizadores en esta extensión son guías de referencia basadas en documentación oficial de MDN, web.dev y Chrome Developers. No representan verdades absolutas sobre cómo los motores de búsqueda evalúan tu contenido—solo los motores de búsqueda conocen sus algoritmos internos. Usa estas recomendaciones como punto de partida para mejorar tu sitio.

Última actualización: 15 de diciembre de 2025

Artículos relacionados

Versión relacionada

Introducción

Caching Headers Explained

El caché HTTP permite a los navegadores almacenar copias de recursos localmente, eliminando la necesidad de descargarlos nuevamente en visitas posteriores

Hub de categoría

Hub

Performance Seo Hub

El rendimiento es un factor de ranking crítico e impacta directamente la experiencia del usuario

En la misma categoría

Guía detallada

Ttfb Optimization Guide

Time to First Byte (TTFB) es la base del rendimiento web—cada milisegundo de TTFB retrasa toda la carga de tu página

Guía detallada

Text Compression Implementation Guide

La compresión de texto puede reducir los tamaños de transferencia en un 60-90%, haciéndola una de las optimizaciones de rendimiento de mayor impacto disponibles

Guía detallada

Render Blocking Resources Optimization Guide

Los recursos que bloquean el renderizado son uno de los problemas de rendimiento más impactantes que puedes corregir