Saltar al contenido principal

Caching

🎯 Objetivo

Regla

12.1: Se debe aprovechar el mecanismo de caché HTTP estándar para mejorar el rendimiento, reducir la carga del servidor y disminuir la latencia para los clientes. 12.2: La implementación correcta de encabezados de caché es obligatoria para respuestas de GET y HEAD que sean cacheables.

📖 Concepto y Definición

Regla

12.3: El caching HTTP permite que las respuestas de la API sean almacenadas temporalmente (en el cliente, en proxies intermedios) y reutilizadas para solicitudes subsecuentes idénticas, evitando contactar al servidor de origen si la copia en caché aún es válida. 12.4: Los mecanismos clave obligatorios para controlar el caching son los encabezados HTTP:

  • Cache-Control (Respuesta): Debe ser el encabezado principal para definir la política de caché. Directivas comunes y obligatorias a usar según corresponda:
    • public: La respuesta puede ser cacheada por cualquier caché (cliente, proxy).
    • private: La respuesta es específica para un usuario y solo debe ser cacheada por el cliente final (navegador), no por proxies compartidos.
    • no-cache: La caché debe revalidar con el servidor de origen antes de usar una copia almacenada (usando ETag o Last-Modified). No significa "no cachear", sino "revalidar siempre".
    • no-store: La respuesta no debe ser almacenada en ninguna caché bajo ninguna circunstancia. Es la directiva más restrictiva.
    • max-age=<segundos>: Especifica el tiempo máximo que la respuesta se considera fresca.
    • s-maxage=<segundos>: Similar a max-age, pero aplica solo a cachés compartidas (proxies).
    • must-revalidate: Indica que la caché debe revalidar una vez que la respuesta se vuelve obsoleta (max-age expira). No debe servir contenido obsoleto.
  • ETag (Respuesta): Debe usarse para caché basada en validación. Es un identificador opaco y único para una versión específica de un recurso. El servidor debe incluirlo en respuestas GET/HEAD.
  • Last-Modified (Respuesta): Puede usarse como alternativa o complemento a ETag, indicando la fecha de última modificación del recurso. Su granularidad es menor que ETag.
  • If-None-Match (Solicitud): El cliente debe enviar el ETag recibido previamente en este encabezado para revalidar su copia en caché. Si el ETag en el servidor coincide, el servidor debe responder con 304 Not Modified (sin cuerpo).
  • If-Modified-Since (Solicitud): El cliente debe enviar la fecha Last-Modified recibida previamente en este encabezado. Si el recurso no ha sido modificado desde esa fecha, el servidor debe responder con 304 Not Modified (sin cuerpo).
  • Vary (Respuesta): Debe usarse para indicar qué encabezados de la solicitud (además de la URI y el método) influyeron en la selección de la representación de la respuesta (ej. Accept, Accept-Language). Esto asegura que diferentes solicitudes (ej. pidiendo JSON vs XML) se cacheen por separado.

🤔 ¿Por qué es Importante? / Beneficios Clave (Contexto)

  • Mejora del Rendimiento Percibido: Respuestas más rápidas para el cliente.
  • Reducción de Carga del Servidor: Menos solicitudes llegan al origen.
  • Ahorro de Ancho de Banda: Se transfiere menos data (especialmente con 304).
  • Escalabilidad: Contribuye a manejar más carga.

✅ Buenas Prácticas y Recomendaciones Clave

Regla

12.5: Las respuestas para solicitudes GET y HEAD deben incluir encabezados Cache-Control y ETag (o Last-Modified) para habilitar el caching y la validación. 12.6: La elección de las directivas Cache-Control (public, private, no-cache, max-age) debe basarse en la naturaleza del recurso:

  • Recursos estáticos o públicos que cambian raramente: Usar public con max-age largo.
  • Recursos específicos de un usuario autenticado: Usar private con max-age adecuado o no-cache (requiriendo revalidación).
  • Recursos que cambian muy frecuentemente o deben estar siempre actualizados: Usar no-cache (para permitir almacenamiento pero forzar revalidación) o no-store (si no debe almacenarse nunca). 12.7: Se debe preferir el uso de ETag sobre Last-Modified para validación, ya que ofrece mayor precisión. 12.8: La generación de ETag debe basarse en el contenido real de la respuesta. Un hash del cuerpo de la respuesta es un método común y recomendado. 12.9: El servidor debe procesar correctamente los encabezados condicionales de la solicitud (If-None-Match, If-Modified-Since) y devolver 304 Not Modified con cuerpo vacío si la condición se cumple. Esto es obligatorio para una implementación eficiente del caché. 12.10: El encabezado Vary debe incluirse si la respuesta depende de otros encabezados además de la URI y el método. Como mínimo, debería incluirse Vary: Accept si se soporta negociación de contenido.

Don'ts (Prohibido):

  • 12.11: No se debe cachear respuestas de métodos no seguros/no idempotentes (POST, PUT, DELETE, PATCH) por defecto, a menos que se indique explícitamente con encabezados Cache-Control y se entiendan las implicaciones.
  • 12.12: No se debe configurar caché agresiva (ej. max-age largo) para recursos que cambian frecuentemente o contienen datos sensibles que podrían quedar obsoletos.
  • 12.13: No se debe omitir el encabezado Vary si la respuesta depende de encabezados de la solicitud (ej. Accept, Accept-Language), ya que podría llevar a servir contenido incorrecto desde la caché.
  • 12.14: No se debe implementar lógica de caché personalizada si los mecanismos estándar HTTP son suficientes.

💡 Ejemplos Prácticos (Ilustrativos)

Ejemplo 1: Respuesta Cacheable con ETag y Max-Age

// Respuesta a GET /products/prod_abc
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: public, max-age=3600 // Cacheable por 1 hora por cualquiera
ETag: "xyz789hash"
Vary: Accept

{
"productId": "prod_abc",
"name": "Widget",
"description": "A standard widget."
}

Ejemplo 2: Solicitud de Revalidación del Cliente

// Solicitud subsecuente para el mismo producto
GET /products/prod_abc HTTP/1.1
Host: api.example.com
Accept: application/json
If-None-Match: "xyz789hash" // Envía el ETag que tenía

Ejemplo 3: Respuesta del Servidor si el Recurso no Cambió

// Si el ETag en el servidor sigue siendo "xyz789hash"
HTTP/1.1 304 Not Modified
Cache-Control: public, max-age=3600
ETag: "xyz789hash"
Vary: Accept
// SIN CUERPO DE RESPUESTA

El cliente debe usar su copia cacheada.

Ejemplo 4: Respuesta para Datos Privados del Usuario

// Respuesta a GET /users/me/profile
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: private, no-cache // Solo caché privada, revalidar siempre
ETag: "user_profile_hash_123"
Vary: Accept

{
"userId": "user123",
"email": "private@example.com",
// ... más datos privados ...
}

🛠️ Herramientas y Consideraciones Adicionales (Contexto)

  • CDNs (Content Delivery Networks): Pueden actuar como cachés compartidas muy eficaces, especialmente para APIs públicas.
  • API Gateways: Muchos ofrecen funcionalidades de caché integradas.
  • Invalidación de Caché: Es uno de los problemas difíciles. El uso de ETag y no-cache o max-age cortos ayuda, pero para invalidaciones activas se pueden requerir estrategias más complejas (no cubiertas por el estándar HTTP básico).
  • Consistencia: Los valores ETag deben ser consistentes entre múltiples instancias del servidor si se usa balanceo de carga.