HTTP 304 Not Modified, simulado petición a petición
HTTP 304 Not Modified
HTTP 304 Not Modified: ETag e If-None-Match explicados | We.Inc
Simula una petición condicional: define el ETag y el Last-Modified del servidor, el If-None-Match y el If-Modified-Since del navegador, y mira si sale 304 o 200 y por qué.
Una respuesta 304 Not Modified le dice al navegador que su copia en caché sigue vigente, así que el servidor no vuelve a enviar el cuerpo. Responde a un GET o HEAD condicional que llevaba If-None-Match o If-Modified-Since. Un 304 es una buena noticia, no un error: ahorra ancho de banda mientras se sigue usando el archivo en caché.
Los pares de validadores detrás de un 304
El servidor envía
El navegador devuelve
El servidor responde 304 cuando
ETag: "v2"
If-None-Match: "v2"
Una de las etiquetas listadas coincide con el ETag actual (comparación débil)
Last-Modified: <date>
If-Modified-Since: <date>
El recurso no ha cambiado desde esa fecha
Ambos
Ambos
Decide If-None-Match; If-Modified-Since se ignora
Rellena lo que tiene ahora el servidor y lo que envía el navegador, y el simulador aplica las reglas de precedencia de la especificación HTTP. Muestra el estado que debería devolver el servidor y qué cabecera lo decidió. Deja un campo vacío para omitir esa cabecera.
Define el ETag y el Last-Modified que tiene el servidor para el archivo en este momento.
Define las cabeceras If-None-Match e If-Modified-Since que envía el navegador desde su caché.
Lee el estado y el motivo: qué cabecera se evaluó y por qué coincidió o no.
Cambia el ETag del servidor para ver cómo un despliegue convierte la siguiente petición en un 200.
Un 304 debe repetir cabeceras como Cache-Control, ETag, Vary y Date que llevaría un 200, para que la caché pueda actualizar sus metadatos guardados.
Los nombres de recursos con huella, como app.3f9a1c.js, más un max-age largo evitan por completo la revalidación. Reserva la revalidación por ETag para el HTML y las respuestas de API.
Los ETag débiles empiezan por W/ y prometen equivalencia semántica, no igualdad de bytes. Siguen funcionando con If-None-Match porque esa cabecera usa comparación débil.
En DevTools, una fila 304 con un tamaño de transferencia pequeño es el navegador revalidando con éxito. Desactiva la caché en el panel Network para forzar respuestas 200 completas mientras depuras.
Preguntas frecuentes
¿Una respuesta 304 es un error?
No. Es un estado de la clase de redirección que significa que se puede usar la copia en caché. El navegador muestra la página desde su caché, y solo las cabeceras han viajado por la red.
¿Una respuesta 304 puede tener cuerpo?
No. La especificación dice que una respuesta 304 termina al final de la sección de cabeceras y no puede contener contenido.
¿Cuál gana, If-None-Match o If-Modified-Since?
If-None-Match. Cuando una petición lleva las dos, un servidor que admite entity tags evalúa If-None-Match e ignora If-Modified-Since, porque las etiquetas detectan cambios que una fecha con precisión de un segundo no puede.
¿Por qué recibo 200 en lugar de 304 después de cada despliegue?
El despliegue cambió el archivo o su ETag, así que la etiqueta que guarda el navegador ya no coincide. Es el comportamiento correcto. Si los archivos sin cambios también pierden su 304, comprueba si tus servidores generan ETag distintos para el mismo contenido.
¿Un POST devuelve alguna vez 304?
No. If-None-Match e If-Modified-Since solo producen 304 con GET y HEAD. Con otros métodos, una condición que falla da 412 Precondition Failed.
Cómo funciona la revalidación
Cuando una respuesta en caché caduca, el navegador no la tira. Pregunta al servidor si su copia sigue siendo válida enviando los validadores que guardó: el ETag como If-None-Match y la fecha Last-Modified como If-Modified-Since.
Si nada cambió, el servidor responde 304 con cabeceras actualizadas y sin cuerpo, y el navegador sigue usando su copia. Si algo cambió, el servidor envía un 200 normal con el contenido nuevo.
Etiquetas fuertes y débiles
Una etiqueta fuerte como "v2" promete que los bytes son idénticos. Una etiqueta débil como W/"v2" solo promete que el contenido es equivalente. If-None-Match compara de forma débil, así que ahí W/"v2" y "v2" cuentan como coincidencia; If-Match compara de forma fuerte y no lo haría.
El intercambio en la red
Primera visita: 200 con validadores
El servidor entrega un ETag y una fecha Last-Modified junto con el contenido.
Siguiente visita: 304 sin cuerpo
El navegador devuelve la etiqueta guardada y recibe solo cabeceras.
Responder tú mismo a peticiones condicionales
Servidor http de Node con un ETag basado en el hash del contenido
Calcula el hash del cuerpo, lo compara con If-None-Match y envía 304 sin cuerpo si coincide.
Simulador de peticiones condicionales
Lo que tiene ahora el servidor
Lo que envía el navegador
ETag
Last-Modified
If-None-Match
If-Modified-Since
Respuesta del servidor
If-None-Match incluye una etiqueta que coincide con el ETag actual en comparación débil, así que la copia en caché está vigente.
If-None-Match es * y el recurso existe, así que la condición es falsa.
Ninguna etiqueta de If-None-Match coincide con el ETag actual, así que el servidor envía el contenido nuevo. If-Modified-Since no se comprueba.
No hay If-None-Match, y Last-Modified no es posterior a If-Modified-Since.
No hay If-None-Match, y Last-Modified es posterior a If-Modified-Since.
If-Modified-Since está presente, pero una de las dos fechas no se puede leer, así que la cabecera se ignora.
La petición no lleva validadores, así que es una petición normal.