¿Cómo controlamos el almacenamiento en caché de la página web en todos los navegadores?


1552

Nuestras investigaciones nos han demostrado que no todos los navegadores respetan las directivas de caché HTTP de manera uniforme.

Por razones de seguridad, no queremos que ciertas páginas de nuestra aplicación sean almacenadas en caché, nunca, por el navegador web. Esto debe funcionar al menos para los siguientes navegadores:

  • Internet Explorer 6+
  • Firefox 1.5+
  • Safari 3+
  • Opera 9+
  • Cromo

Nuestro requisito vino de una prueba de seguridad. Después de cerrar sesión en nuestro sitio web, puede presionar el botón Atrás y ver las páginas en caché.


Solo para ipad Safari, ¿ayuda [esto] [1]? [1]: stackoverflow.com/questions/24524248/…
Bakhshi

Lo más simple es usar: max-age = 10. Esto no es perfecto porque la página se almacenará en caché durante 10 segundos. Pero es la solución menos "espagueti de cabecera" que existe. Además, esto a veces proporciona un gran aumento de rendimiento en sitios web dinámicos que usan proxies inversos. (Su secuencia de comandos php lenta se llamará una vez cada 10 segundos y luego será almacenada en caché por el proxy inverso. Una vez cada 10 segundos es mucho mejor que una vez por visitante)
Hello World,


3
Gracias por esa gran pregunta. Por curiosidad, ¿cuál podría ser la situación que le hace enviar algunos datos mientras no desea que el receptor los guarde por "razones de seguridad" ? ya los enviaste!
Contador م

1
@Contador: en su caso, el usuario había cerrado la sesión. ¿Quién puede garantizar que el próximo usuario humano en ese User-Agent será la persona que acaba de cerrar sesión?
Fabien Haddadi

Respuestas:


2579

Introducción

El conjunto mínimo correcto de encabezados que funciona en todos los clientes mencionados (y servidores proxy):

Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0

El Cache-Controles por la especificación HTTP 1.1 para clientes y proxies (y es requerido implícitamente por algunos clientes al lado Expires) El Pragmaes por la especificación HTTP 1.0 para clientes prehistóricos. El Expireses según las especificaciones HTTP 1.0 y 1.1 para clientes y proxies. En HTTP 1.1, Cache-Controlprevalece sobre Expires, por lo que es, después de todo, solo para proxies HTTP 1.0.

Si no le importa IE6 y su almacenamiento en caché roto cuando solo sirve páginas a través de HTTPS no-store, puede omitirlo Cache-Control: no-cache.

Cache-Control: no-store, must-revalidate
Pragma: no-cache
Expires: 0

Si no le interesan los clientes IE6 ni HTTP 1.0 (HTTP 1.1 se introdujo en 1997), puede omitirlo Pragma.

Cache-Control: no-store, must-revalidate
Expires: 0

Si tampoco te interesan los proxys HTTP 1.0, entonces puedes omitirlos Expires.

Cache-Control: no-store, must-revalidate

Por otro lado, si el servidor incluye automáticamente un Dateencabezado válido , teóricamente Cache-Controltambién podría omitirlo y confiar Expiressolo en él.

Date: Wed, 24 Aug 2016 18:32:02 GMT
Expires: 0

Pero eso puede fallar si, por ejemplo, el usuario final manipula la fecha del sistema operativo y el software del cliente depende de ella.

Otros Cache-Controlparámetros como max-ageson irrelevantes si Cache-Controlse especifican los parámetros mencionados anteriormente . El Last-Modifiedencabezado como se incluye en la mayoría de las otras respuestas aquí solo es interesante si realmente desea almacenar en caché la solicitud, por lo que no necesita especificarla en absoluto.

¿Cómo configurarlo?

Usando PHP:

header("Cache-Control: no-cache, no-store, must-revalidate"); // HTTP 1.1.
header("Pragma: no-cache"); // HTTP 1.0.
header("Expires: 0"); // Proxies.

Usando Java Servlet, o Node.js:

response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
response.setHeader("Expires", "0"); // Proxies.

Usando ASP.NET-MVC

Response.Cache.SetCacheability(HttpCacheability.NoCache);  // HTTP 1.1.
Response.Cache.AppendCacheExtension("no-store, must-revalidate");
Response.AppendHeader("Pragma", "no-cache"); // HTTP 1.0.
Response.AppendHeader("Expires", "0"); // Proxies.

Usando ASP.NET Web API:

// `response` is an instance of System.Net.Http.HttpResponseMessage
response.Headers.CacheControl = new CacheControlHeaderValue
{
    NoCache = true,
    NoStore = true,
    MustRevalidate = true
};
response.Headers.Pragma.ParseAdd("no-cache");
// We can't use `response.Content.Headers.Expires` directly
// since it allows only `DateTimeOffset?` values.
response.Content?.Headers.TryAddWithoutValidation("Expires", 0.ToString()); 

Usando ASP.NET:

Response.AppendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
Response.AppendHeader("Pragma", "no-cache"); // HTTP 1.0.
Response.AppendHeader("Expires", "0"); // Proxies.

Usando ASP.NET Core v3

// using Microsoft.Net.Http.Headers
Response.Headers[HeaderNames.CacheControl] = "no-cache, no-store, must-revalidate";
Response.Headers[HeaderNames.Expires] = "0";
Response.Headers[HeaderNames.Pragma] = "no-cache";

Usando ASP:

Response.addHeader "Cache-Control", "no-cache, no-store, must-revalidate" ' HTTP 1.1.
Response.addHeader "Pragma", "no-cache" ' HTTP 1.0.
Response.addHeader "Expires", "0" ' Proxies.

Usando Ruby on Rails, o Python / Flask:

headers["Cache-Control"] = "no-cache, no-store, must-revalidate" # HTTP 1.1.
headers["Pragma"] = "no-cache" # HTTP 1.0.
headers["Expires"] = "0" # Proxies.

Usando Python / Django:

response["Cache-Control"] = "no-cache, no-store, must-revalidate" # HTTP 1.1.
response["Pragma"] = "no-cache" # HTTP 1.0.
response["Expires"] = "0" # Proxies.

Usando Python / Pyramid:

request.response.headerlist.extend(
    (
        ('Cache-Control', 'no-cache, no-store, must-revalidate'),
        ('Pragma', 'no-cache'),
        ('Expires', '0')
    )
)

Usando Go:

responseWriter.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") // HTTP 1.1.
responseWriter.Header().Set("Pragma", "no-cache") // HTTP 1.0.
responseWriter.Header().Set("Expires", "0") // Proxies.

Usando el .htaccessarchivo Apache :

<IfModule mod_headers.c>
    Header set Cache-Control "no-cache, no-store, must-revalidate"
    Header set Pragma "no-cache"
    Header set Expires 0
</IfModule>

Usando HTML4:

<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">

Metaetiquetas HTML frente a encabezados de respuesta HTTP

Es importante saber que cuando se sirve una página HTML a través de una conexión HTTP, y un encabezado está presente tanto en los encabezados de respuesta HTTP como en las <meta http-equiv>etiquetas HTML , entonces el especificado en el encabezado de respuesta HTTP tendrá prioridad sobre la metaetiqueta HTML. La metaetiqueta HTML solo se utilizará cuando la página se vea desde un sistema de archivos de disco local a través de una file://URL. Ver también W3 HTML spec capítulo 5.2.2 . Tenga cuidado con esto cuando no los especifique mediante programación porque el servidor web puede incluir algunos valores predeterminados.

En general, es mejor que no especifique las metaetiquetas HTML para evitar confusiones por parte de los principiantes y confiar en los encabezados de respuesta HTTP. Además, específicamente esas <meta http-equiv>etiquetas no son válidas en HTML5. Solo se permiten los http-equivvalores enumerados en la especificación HTML5 .

Verificación de los encabezados de respuesta HTTP reales

Para verificar uno y otro, puede verlos / depurarlos en el monitor de tráfico HTTP del conjunto de herramientas de desarrollo del navegador web. Puede llegar presionando F12 en Chrome / Firefox23 + / IE9 + y luego abriendo el panel de pestañas "Red" o "Red", y luego haciendo clic en la solicitud HTTP de interés para descubrir todos los detalles sobre la solicitud y respuesta HTTP. La siguiente captura de pantalla es de Chrome:

Conjunto de herramientas para desarrolladores de Chrome Monitor de tráfico HTTP que muestra encabezados de respuesta HTTP en stackoverflow.com

También quiero configurar esos encabezados en las descargas de archivos

En primer lugar, esta pregunta y respuesta están dirigidas a "páginas web" (páginas HTML), no a "descargas de archivos" (PDF, zip, Excel, etc.). Será mejor que los guarde en caché y utilice algún identificador de versión de archivo en algún lugar de la ruta URI o cadena de consulta para forzar una descarga en un archivo modificado. Cuando aplique esos encabezados sin caché en las descargas de archivos de todos modos, tenga cuidado con el error IE7 / 8 al realizar una descarga de archivos a través de HTTPS en lugar de HTTP. Para más detalles, consulte IE no puede descargar foo.jsf. IE no pudo abrir este sitio de Internet. El sitio solicitado no está disponible o no se puede encontrar .


16
Esto no parece estar completo. Probé esta solución en IE 8 y descubrí que el navegador cargará una versión en caché cuando presione el botón Atrás.
Mike Ottum

16
Probablemente su metodología de prueba estaba equivocada. Tal vez la página ya estaba en el caché? ¿Quizás los encabezados eran incorrectos / anulados? Tal vez estabas mirando la solicitud incorrecta? Etc ..
BalusC

8
En realidad, confirmo que este enfoque es incompleto y causa problemas con IE8, o al menos en algunas circunstancias. Específicamente, cuando se usa IE8 para recuperar un recurso a través de SSL, IE8 se negará a recuperar el recurso por segunda vez (ya sea, o después de un primer intento, dependiendo de los encabezados utilizados). Ver el blog de EricLaw , por ejemplo.
haylem

21
Me gustaría agregar que esto es esencialmente lo que hace Bank of America. Si observa sus encabezados de respuesta y los traduce a aspx, están haciendo: Response.AppendHeader ("Cache-Control", "no-cache, no-store, must-revalidate"); Response.AppendHeader ("Expires", "Thu, 01 Dec 1994 16:00:00 GMT"); Me imagino que si es lo suficientemente bueno para ellos, es lo suficientemente bueno para mí.
John

8
@John: el encabezado que caduca es exactamente el valor de ejemplo en la especificación HTTP 1.0 . Funciona, pero es algo ridículo tomar exactamente esa marca de tiempo.
BalusC

244

(Hola, todos: por favor, no solo copie y pegue sin pensar todos los encabezados que pueda encontrar)

En primer lugar, el historial del botón Atrás no es un caché :

El modelo de frescura (Sección 4.2) no se aplica necesariamente a los mecanismos de historia. Es decir, un mecanismo de historial puede mostrar una representación previa incluso si ha expirado.

En la antigua especificación HTTP, la redacción era aún más fuerte, indicando explícitamente a los navegadores que ignoraran las directivas de caché para el historial del botón de retroceso.

Atrás se supone que retroceder en el tiempo (hasta el momento en que el usuario se registra en). No navega hacia adelante a una URL abierta previamente.

Sin embargo, en la práctica, el caché puede influir en el botón Atrás, en circunstancias muy específicas:

  • La página debe entregarse a través de HTTPS ; de lo contrario, este bloqueo de caché no será confiable. Además, si no está utilizando HTTPS, su página es vulnerable al robo de inicio de sesión de muchas otras maneras.
  • Debe enviar Cache-Control: no-store, must-revalidate(algunos navegadores observan no-storey otros observan must-revalidate)

Usted no necesita ninguna de:

  • <meta>con encabezados de caché: no funciona en absoluto. Totalmente inútil.
  • post-check/ pre-check- es la directiva solo de IE que solo se aplica a los recursos en caché .
  • Enviar el mismo encabezado dos veces o en docenas de partes. Algunos fragmentos de PHP existentes reemplazan los encabezados anteriores, lo que resulta en que solo se envíe el último.

Si lo desea, puede agregar:

  • no-cacheo max-age=0, lo que hará que el recurso (URL) "caduque" y requiera que los navegadores verifiquen con el servidor si hay una versión más nueva ( no-storeya implica que esto es aún más fuerte).
  • Expirescon una fecha en el pasado para clientes HTTP / 1.0 (aunque los clientes reales solo HTTP / 1.0 son completamente inexistentes en estos días).

Bonificación: El nuevo almacenamiento en caché HTTP RFC .


1
¿Tendrá esto algún efecto secundario en el rendimiento del sitio web en términos de tiempo de carga? ¿Cómo no-store, no-cache, must-revalidate afectar el rendimiento?
Raman Ghai

@RamanGhai Desactivar el caché generalmente perjudica el rendimiento (y las 3 opciones que ha mencionado desactivan el almacenamiento en caché). Puede hacer que los CDN y los servidores proxy de ISP (p. Ej., Utilizados comúnmente por los operadores móviles) sean ineficaces. No perjudica la primera carga de un nuevo usuario (aparte del problema del proxy), pero luego la navegación posterior puede ser mucho más lenta.
Kornel

@porneL declaras que debemos enviar Cache-Control: must-revalidate. ¿Por qué no enviar Cache-Control: no-cacheya que no-cacheya implica must-revalidate? w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.1
Pacerier

3
@Pacerier la relación de no-cachecon must-revalidatees verdadera para el caché, pero el historial anterior no es un caché. Navegadores de casos especiales explícitos must-revalidatepara controlar el comportamiento del historial .
Kornel

@porneL, Hmm, ¿hay un RFC de apoyo que indique que ese es el comportamiento deseado?
Pacerier

103

Como dijo @Kornel, lo que quiere no es desactivar el caché, sino desactivar el búfer de historial. Los diferentes navegadores tienen sus propias formas sutiles de deshabilitar el búfer de historial.

En Chrome (v28.0.1500.95 m) podemos hacer esto solo por Cache-Control: no-store.

En Firefox (v23.0.1) cualquiera de estos funcionará:

  1. Cache-Control: no-store

  2. Cache-Control: no-cache (solo https)

  3. Pragma: no-cache (solo https)

  4. Vary: * (solo https)

En Opera (v12.15) solo podemos hacer esto Cache-Control: must-revalidate(solo https).

En Safari (v5.1.7, 7534.57.2) cualquiera de estos funcionará:

  1. Cache-Control: no-store
    <body onunload=""> en html

  2. Cache-Control: no-store (solo https)

En IE8 (v8.0.6001.18702IC) cualquiera de estos funcionará:

  1. Cache-Control: must-revalidate, max-age=0

  2. Cache-Control: no-cache

  3. Cache-Control: no-store

  4. Cache-Control: must-revalidate
    Expires: 0

  5. Cache-Control: must-revalidate
    Expires: Sat, 12 Oct 1991 05:00:00 GMT

  6. Pragma: no-cache (solo https)

  7. Vary: * (solo https)

La combinación de lo anterior nos da esta solución que funciona para Chrome 28, FireFox 23, IE8, Safari 5.1.7 y Opera 12.15: Cache-Control: no-store, must-revalidate (solo https)

Tenga en cuenta que https es necesario porque Opera no desactivaría el búfer de historial para páginas http simples. Si realmente no puede obtener https y está preparado para ignorar Opera, lo mejor que puede hacer es esto:

Cache-Control: no-store
<body onunload="">

A continuación se muestran los registros sin procesar de mis pruebas:

HTTP:

  1. Cache-Control: private, no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Fallo: Opera 12.15
    Éxito: Chrome 28, FireFox 23, IE8, Safari 5.1.7

  2. Cache-Control: private, no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Fallo: Opera 12.15
    Éxito: Chrome 28, FireFox 23, IE8, Safari 5.1.7

  3. Cache-Control: private, no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    Error: Safari 5.1.7, Opera 12.15
    Éxito: Chrome 28, FireFox 23, IE8

  4. Cache-Control: private, no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    Error: Safari 5.1.7, Opera 12.15
    Éxito: Chrome 28, FireFox 23, IE8

  5. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Falla: Chrome 28, FireFox 23, Safari 5.1.7, Opera 12.15
    Éxito: IE8

  6. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Falla: Chrome 28, FireFox 23, Safari 5.1.7, Opera 12.15
    Éxito: IE8

  7. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Falla: Chrome 28, FireFox 23, Safari 5.1.7, Opera 12.15
    Éxito: IE8

  8. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Falla: Chrome 28, FireFox 23, Safari 5.1.7, Opera 12.15
    Éxito: IE8

  9. Cache-Control: no-store
    Error: Safari 5.1.7, Opera 12.15
    Éxito: Chrome 28, FireFox 23, IE8

  10. Cache-Control: no-store
    <body onunload="">
    Fallo: Opera 12.15
    Éxito: Chrome 28, FireFox 23, IE8, Safari 5.1.7

  11. Cache-Control: no-cache
    Falla: Chrome 28, FireFox 23, Safari 5.1.7, Opera 12.15
    Éxito: IE8

  12. Vary: *
    Fallo: Chrome 28, FireFox 23, IE8, Safari 5.1.7, Opera 12.15
    Éxito: ninguno

  13. Pragma: no-cache
    Fallo: Chrome 28, FireFox 23, IE8, Safari 5.1.7, Opera 12.15
    Éxito: ninguno

  14. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Falla: Chrome 28, FireFox 23, Safari 5.1.7, Opera 12.15
    Éxito: IE8

  15. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Falla: Chrome 28, FireFox 23, Safari 5.1.7, Opera 12.15
    Éxito: IE8

  16. Cache-Control: must-revalidate, max-age=0
    Falla: Chrome 28, FireFox 23, Safari 5.1.7, Opera 12.15
    Éxito: IE8

  17. Cache-Control: must-revalidate
    Expires: 0
    Falla: Chrome 28, FireFox 23, Safari 5.1.7, Opera 12.15
    Éxito: IE8

  18. Cache-Control: must-revalidate
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Falla: Chrome 28, FireFox 23, Safari 5.1.7, Opera 12.15
    Éxito: IE8

  19. Cache-Control: private, must-revalidate, proxy-revalidate, s-maxage=0
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Fallo: Chrome 28, FireFox 23, IE8, Safari 5.1.7, Opera 12.15
    Éxito: ninguno

HTTPS:

  1. Cache-Control: private, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    <body onunload="">
    Fallo: Chrome 28, FireFox 23, IE8, Safari 5.1.7, Opera 12.15
    Éxito: ninguno

  2. Cache-Control: private, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    <body onunload="">
    Fallo: Chrome 28, FireFox 23, IE8, Safari 5.1.7, Opera 12.15
    Éxito: ninguno

  3. Vary: *
    Fallo: Chrome 28, Safari 5.1.7, Opera 12.15
    Éxito: FireFox 23, IE8

  4. Pragma: no-cache
    Fallo: Chrome 28, Safari 5.1.7, Opera 12.15
    Éxito: FireFox 23, IE8

  5. Cache-Control: no-cache
    Fallo: Chrome 28, Safari 5.1.7, Opera 12.15
    Éxito: FireFox 23, IE8

  6. Cache-Control: private, no-cache, max-age=0, proxy-revalidate, s-maxage=0
    Fallo: Chrome 28, Safari 5.1.7, Opera 12.15
    Éxito: FireFox 23, IE8

  7. Cache-Control: private, no-cache, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    Fallo: Chrome 28, Safari 5.1.7, Opera 12.15
    Éxito: FireFox 23, IE8

  8. Cache-Control: private, no-cache, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    Fallo: Chrome 28, Safari 5.1.7, Opera 12.15
    Éxito: FireFox 23, IE8

  9. Cache-Control: must-revalidate
    Fallo: Chrome 28, FireFox 23, IE8, Safari 5.1.7
    Éxito: Opera 12.15

  10. Cache-Control: private, must-revalidate, proxy-revalidate, s-maxage=0
    <body onunload="">
    Fallo: Chrome 28, FireFox 23, IE8, Safari 5.1.7
    Éxito: Opera 12.15

  11. Cache-Control: must-revalidate, max-age=0
    Fallo: Chrome 28, FireFox 23, Safari 5.1.7
    Éxito: IE8, Opera 12.15

  12. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Falla: Chrome 28, Safari 5.1.7
    Éxito: FireFox 23, IE8, Opera 12.15

  13. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Falla: Chrome 28, Safari 5.1.7
    Éxito: FireFox 23, IE8, Opera 12.15

  14. Cache-Control: no-store
    Fallo: Opera 12.15
    Éxito: Chrome 28, FireFox 23, IE8, Safari 5.1.7

  15. Cache-Control: private, no-cache, no-store, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Fallo: Opera 12.15
    Éxito: Chrome 28, FireFox 23, IE8, Safari 5.1.7

  16. Cache-Control: private, no-cache, no-store, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Fallo: Opera 12.15
    Éxito: Chrome 28, FireFox 23, IE8, Safari 5.1.7

  17. Cache-Control: private, no-cache
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    Fallo: Chrome 28, Safari 5.1.7, Opera 12.15
    Éxito: FireFox 23, IE8

  18. Cache-Control: must-revalidate
    Expires: 0
    Falla: Chrome 28, FireFox 23, Safari 5.1.7,
    Éxito: IE8, Opera 12.15

  19. Cache-Control: must-revalidate
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Falla: Chrome 28, FireFox 23, Safari 5.1.7,
    Éxito: IE8, Opera 12.15

  20. Cache-Control: private, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    <body onunload="">
    Falla: Chrome 28, FireFox 23, Safari 5.1.7,
    Éxito: IE8, Opera 12.15

  21. Cache-Control: private, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    <body onunload="">
    Falla: Chrome 28, FireFox 23, Safari 5.1.7,
    Éxito: IE8, Opera 12.15

  22. Cache-Control: private, must-revalidate
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    Falla: Chrome 28, Safari 5.1.7
    Éxito: FireFox 23, IE8, Opera 12.15

  23. Cache-Control: no-store, must-revalidate
    Fallo: ninguno
    Éxito: Chrome 28, FireFox 23, IE8, Safari 5.1.7, Opera 12.15


3
Sé que esto fue publicado hace un par de años, pero fue una lectura interesante. Este problema me ha estado volviendo loco durante unos meses, el cuerpo parece saber realmente cómo lidiar con el control de caché. He visto a algunas personas usar el, <body onunload="">pero parece más una forma de evitar el problema real. He intentado usar .htaccess y modificar los encabezados de esa manera, si uso HTTPS ¿debería funcionar de esa manera? Principalmente es el safari donde más se presenta el problema.
Jordania

44
@ Jordan, según los registros anteriores, si tiene HTTPS, entonces agregar Cache-Control: no-storeharía el truco. <body onunload="">solo es necesario cuando no tienes HTTPS.
Pacerier

37

Encontré la ruta web.config útil (traté de agregarla a la respuesta pero no parece haber sido aceptada, por lo que publico aquí)

<configuration>
<system.webServer>
    <httpProtocol>
        <customHeaders>
            <add name="Cache-Control" value="no-cache, no-store, must-revalidate" />
            <!-- HTTP 1.1. -->
            <add name="Pragma" value="no-cache" />
            <!-- HTTP 1.0. -->
            <add name="Expires" value="0" />
            <!-- Proxies. -->
        </customHeaders>
    </httpProtocol>
</system.webServer>

Y aquí está la forma express / node.js de hacer lo mismo:

app.use(function(req, res, next) {
    res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
    res.setHeader('Pragma', 'no-cache');
    res.setHeader('Expires', '0');
    next();
});

Para web.config, modificaría solo un poco para aplicar los encabezados personalizados solo para aquellos scripts que sabemos que se cargan dinámicamente / usando requirejs. Asumiendo que sus scripts se encuentran en la carpeta del cliente: <location path = "client"> ..... </location>
Ibrahim ben Salah

Para quién se pregunta qué web.confes: es la configuración principal y el archivo de configuración para una ASP.NETaplicación web. Es un documento XML que reside en el directorio raíz. ( wiki ).
otro

27

Descubrí que todas las respuestas en esta página todavía tenían problemas. En particular, noté que ninguno de ellos evitaría que IE8 usara una versión en caché de la página cuando accediera presionando el botón Atrás.

Después de mucha investigación y pruebas, descubrí que los únicos dos encabezados que realmente necesitaba eran:

Control de caché: sin tienda
Variar: *

Para obtener una explicación del encabezado Vary, consulte http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.6

En IE6-8, FF1.5-3.5, Chrome 2-3, Safari 4 y Opera 9-10, estos encabezados hicieron que se solicitara la página del servidor cuando hace clic en un enlace a la página o coloca la URL directamente en la barra de direcciones. Eso cubre aproximadamente el 99% de todos los navegadores en uso a partir de enero '10.

En IE6 y Opera 9-10, al presionar el botón Atrás todavía se cargaba la versión en caché. En todos los demás navegadores que probé, obtuvieron una versión nueva del servidor. Hasta ahora, no he encontrado ningún conjunto de encabezados que haga que esos navegadores no devuelvan versiones en caché de las páginas cuando presionas el botón Atrás.

Actualización: Después de escribir esta respuesta, me di cuenta de que nuestro servidor web se identifica a sí mismo como un servidor HTTP 1.0. Los encabezados que he enumerado son los correctos para que los navegadores no almacenen en caché las respuestas de un servidor HTTP 1.0. Para un servidor HTTP 1.1, mire la respuesta de BalusC .


44
¡Esto funciona para el botón de retroceso de IE8! Después de intentar todo en cualquier otra sugerencia, agregar el encabezado "Vary: *" es aparentemente lo único que puede obligar a IE8 a volver a cargar la página cuando el usuario presiona el botón Atrás. Y esto hace el trabajo en HTTP / 1.1 servidores.
coredumperror

En combinación con los encabezados sugeridos por BarlusC, más un fragmento de JS que llama a window.location.reload () cuando el evento onPageShow se activa con el atributo "persistente" (necesario para Safari), cada navegador que he probado con éxito fuerza una recarga desde el servidor cuando el usuario usa el botón Atrás.
coredumperror

1
@CoreDumpError, oh, no debes asumir que JavaScript está habilitado.
Pacerier

@Pacerier En el momento en que escribí la respuesta en 2010, esto funcionó en lo que entonces eran las últimas versiones de Safari y Opera, con nuestro servidor identificándose como un servidor HTTP 1.0. Desafortunadamente, ya no tengo ninguna forma de probar esto fácilmente, así que no puedo decir nada definitivo sobre las últimas versiones de estos navegadores.
Chris Vasselli

¿Cuáles fueron las versiones de navegador que probó?
Pacerier

21

Después de un poco de investigación, se nos ocurrió la siguiente lista de encabezados que parecían cubrir la mayoría de los navegadores:

En ASP.NET los agregamos usando el siguiente fragmento:

Response.ClearHeaders(); 
Response.AppendHeader("Cache-Control", "no-cache"); //HTTP 1.1
Response.AppendHeader("Cache-Control", "private"); // HTTP 1.1
Response.AppendHeader("Cache-Control", "no-store"); // HTTP 1.1
Response.AppendHeader("Cache-Control", "must-revalidate"); // HTTP 1.1
Response.AppendHeader("Cache-Control", "max-stale=0"); // HTTP 1.1 
Response.AppendHeader("Cache-Control", "post-check=0"); // HTTP 1.1 
Response.AppendHeader("Cache-Control", "pre-check=0"); // HTTP 1.1 
Response.AppendHeader("Pragma", "no-cache"); // HTTP 1.0 
Response.AppendHeader("Expires", "Mon, 26 Jul 1997 05:00:00 GMT"); // HTTP 1.0 

Encontrado en: http://forums.asp.net/t/1013531.aspx


36
@bart: Aún más problemático es que el 26 de julio de 1997 fue un sábado, no un lunes ...
Cᴏʀʏ

55
Cache-Control: no-cachey Cache-Control: privatechoque: nunca debes unir ambos: el primero le dice a los navegadores y proxies que no se almacenen en caché, el segundo le dice a los proxies que no se almacenen en caché, pero permite que los navegadores tengan su propia copia privada. No estoy seguro de qué configuración seguirá el navegador, pero es poco probable que sea coherente entre los navegadores y las versiones.
Keith

No utilice la verificación previa y posterior. blogs.msdn.com/b/ieinternals/archive/2009/07/20/…
EricLaw

esto no funcionó para mí: el uso de asp.net 4.5 ejecuta el código pero no produce el resultado requerido. Tuve que seguir esto: stackoverflow.com/questions/22443932/…
Andy

8

El uso del encabezado pragma en la respuesta es un cuento de esposas. RFC2616 solo lo define como un encabezado de solicitud

http://www.mnot.net/cache_docs/#PRAGMA


44
Este es un buen ejemplo de por qué necesita ir más allá de las especificaciones. Si las especificaciones siempre fueran claras como el cristal, no tendría mucho sentido para sitios como StackOverflow. De Microsoft Para fines de compatibilidad con los servidores HTTP 1.0, Internet Explorer admite un uso especial de HTTP Pragma: encabezado sin caché. Si el cliente se comunica con el servidor a través de una conexión segura (https: //) y el servidor devuelve un encabezado Pragma: sin caché con la respuesta, Internet Explorer no almacena en caché la respuesta.
michaelok

@michaelok: su referencia es válida, pero pierde el punto más grande: establezca un control de caché / caduca adecuado y no necesita pragma.
EricLaw

8

DESCARGO DE RESPONSABILIDAD: le recomiendo leer la respuesta de @ BalusC. Después de leer el siguiente tutorial de almacenamiento en caché: http://www.mnot.net/cache_docs/ (le recomiendo que también lo lea), creo que es correcto. Sin embargo, por razones históricas (y porque lo he probado yo mismo), incluiré mi respuesta original a continuación:


Intenté la respuesta 'aceptada' para PHP, que no funcionó para mí. Luego investigué un poco, encontré una ligera variante, la probé y funcionó. Aquí está:

header('Cache-Control: no-store, private, no-cache, must-revalidate');     // HTTP/1.1
header('Cache-Control: pre-check=0, post-check=0, max-age=0, max-stale = 0', false);  // HTTP/1.1
header('Pragma: public');
header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');                  // Date in the past  
header('Expires: 0', false); 
header('Last-Modified: '.gmdate('D, d M Y H:i:s') . ' GMT');
header ('Pragma: no-cache');

Eso debería funcionar. El problema era que al configurar la misma parte del encabezado dos veces, si falseno se envía como segundo argumento a la función de encabezado, la función de encabezado simplemente sobrescribirá la header()llamada anterior . Entonces, al configurar Cache-Control, por ejemplo, si uno no quiere poner todos los argumentos en una header()llamada de función, debe hacer algo como esto:

header('Cache-Control: this');
header('Cache-Control: and, this', false);

Ver documentación más completa aquí .


14
Esto está lleno de mitos. pre-check y post-check son solo para IE, relevantes solo para respuestas en caché, y el valor 0 es un no-op. max-stale es el encabezado de solicitud de proxy, no el encabezado de respuesta del servidor. Caduca acepta solo valor único. Más de uno hará que se ignore este encabezado.
Kornel

1
@porneL, ¿enviarás una respuesta competitiva que aborde estos mitos correctamente?
Pensamiento extraño

@Oddthinking, parece que stackoverflow.com/questions/49547/… es una respuesta competitiva.
Mike Ottum

@Pacerier sí, como digo en el descargo de responsabilidad, use la respuesta de BalusC.
Steven Oxley

8

Para ASP.NET Core, cree una clase de middleware simple:

public class NoCacheMiddleware
{
    private readonly RequestDelegate m_next;

    public NoCacheMiddleware( RequestDelegate next )
    {
        m_next = next;
    }

    public async Task Invoke( HttpContext httpContext )
    {
        httpContext.Response.OnStarting( ( state ) =>
        {
            // ref: http://stackoverflow.com/questions/49547/making-sure-a-web-page-is-not-cached-across-all-browsers
            httpContext.Response.Headers.Append( "Cache-Control", "no-cache, no-store, must-revalidate" );
            httpContext.Response.Headers.Append( "Pragma", "no-cache" );
            httpContext.Response.Headers.Append( "Expires", "0" );
            return Task.FromResult( 0 );
        }, null );

        await m_next.Invoke( httpContext );
    }
}

luego regístralo con Startup.cs

app.UseMiddleware<NoCacheMiddleware>();

Asegúrese de agregar esto en algún lugar después

app.UseStaticFiles();

Sugeriría usar constantes de Microsoft.Net.Http.Headers.HeaderNames en lugar de los literales de cadena "Cache-Controls", "Pragma" y "Expires".
Victor Sharovatov

7

Estas directivas no mitigan ningún riesgo de seguridad. Realmente están destinados a obligar a los UA a actualizar la información volátil, no a evitar que los UA retengan información. Ver esta pregunta similar . Por lo menos, no hay garantía de que ningún enrutador, proxy, etc. ignore las directivas de almacenamiento en caché.

En una nota más positiva, las políticas relacionadas con el acceso físico a las computadoras, la instalación de software y similares lo pondrán millas por delante de la mayoría de las empresas en términos de seguridad. Si los consumidores de esta información son miembros del público, lo único que realmente puede hacer es ayudarlos a comprender que una vez que la información llega a su máquina, esa máquina es su responsabilidad, no la suya.


7

Hay un error en IE6

El contenido con "Content-Encoding: gzip" siempre se almacena en caché, incluso si usa "Cache-Control: no-cache".

http://support.microsoft.com/kb/321722

Puede deshabilitar la compresión gzip para usuarios de IE6 (verifique el agente de usuario para "MSIE 6")


6

El RFC para HTTP 1.1 dice que el método adecuado es agregar un encabezado HTTP para:

Control de caché: sin caché

Los navegadores más antiguos pueden ignorar esto si no cumplen correctamente con HTTP 1.1. Para aquellos que pueden probar el encabezado:

Pragma: sin caché

También se supone que esto funciona para los navegadores HTTP 1.1.


1
La especificación indica que la respuesta no debe reutilizarse sin revalidación. Es el Cache-Control: no-store, que es el método oficial para indicar que la respuesta ni siquiera se almacena en un caché en primer lugar.
AnthonyWJones

6

Establecer el encabezado http modificado en alguna fecha en 1995 generalmente hace el truco.

Aquí hay un ejemplo:

Expira: mié, 15 nov 1995 04:58:08 GMT
Última modificación: mié, 15 de noviembre de 1995 04:58:08 GMT
Cache-Control: no-cache, must-revalidate

1
Establecer una Última modificación hace mucho tiempo no tiene impacto en el almacenamiento en caché, aparte de permitir que una respuesta en caché se use por más tiempo debido a la revalidación heurística.
EricLaw

6

La documentación de PHP para la función de encabezado tiene un ejemplo bastante completo (aportado por un tercero):

    header('Pragma: public');
    header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");                  // Date in the past   
    header('Last-Modified: '.gmdate('D, d M Y H:i:s') . ' GMT');
    header('Cache-Control: no-store, no-cache, must-revalidate');     // HTTP/1.1
    header('Cache-Control: pre-check=0, post-check=0, max-age=0', false);    // HTTP/1.1
    header ("Pragma: no-cache");
    header("Expires: 0", false);

11
Esto obviamente está mal. Las segundas llamadas a header () para Expires, Cache-control y Pragma sobrescriben completamente los valores establecidos previamente.
Kornel

1
@porneL: No, no sobrescriba los valores establecidos previamente, ya que pasa falso como un segundo parámetro, indicando que no anule los valores anteriores.
Julien Palard

1
@JulienPalard la respuesta ha sido editada después de que hice mi comentario. Todavía no tiene mucho sentido.
Kornel

No envíe varios encabezados de Cache-Control si desea trabajar en IE antes del 9. No envíe NUNCA verificación previa o posterior. blogs.msdn.com/b/ieinternals/archive/2009/07/20/…
EricLaw

6

Si tiene problemas de descarga con IE6-IE8 sobre SSL y caché: encabezado sin caché (y valores similares) con archivos de MS Office, puede usar caché: privado, encabezado sin almacenamiento y archivo de retorno en la solicitud POST. Funciona.


6

en mi caso solucioné el problema en Chrome con esto

<form id="form1" runat="server" autocomplete="off">

donde necesito borrar el contenido de los datos de un formulario anterior cuando los usuarios vuelven a presionar el botón por razones de seguridad


Mi problema con el navegador mozilla 19.x también se resolvió mediante el fragmento de código. autocompletar = "apagado". Gracias.
Sábado

5

La respuesta aceptada no parece funcionar para IIS7 +, pasando por la gran cantidad de preguntas sobre los encabezados de caché que no se envían en II7:

Y así

La respuesta aceptada es correcta en qué encabezados deben establecerse, pero no en cómo deben establecerse. De esta manera funciona con IIS7:

Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.AppendCacheExtension("no-store, must-revalidate");
Response.AppendHeader("Pragma", "no-cache");
Response.AppendHeader("Expires", "-1");

La primera línea se establece Cache-controlen no-cache, y la segunda línea agrega los otros atributosno-store, must-revalidate


Esto funciona para mí:Response.Cache.SetAllowResponseInBrowserHistory(false); Response.Cache.SetCacheability(HttpCacheability.NoCache); Response.Cache.SetNoStore(); Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
Vilx-

4

He tenido los mejores y más consistentes resultados en todos los navegadores configurando Pragma: no-cache


4

Los encabezados en la respuesta proporcionada por BalusC no evitan que Safari 5 (y posiblemente también versiones anteriores) muestren contenido de la memoria caché del navegador cuando se usa el botón Atrás del navegador. Una forma de evitar esto es agregar un atributo de controlador de eventos onunload vacío a la etiqueta del cuerpo:

<body onunload=""> 

Este truco aparentemente rompe la memoria caché hacia atrás en Safari: ¿hay un evento de carga entre navegadores al hacer clic en el botón Atrás?


Genial, lo he probado y esto realmente funciona en Safari (5.1.7) pero no en Opera.
Pacerier

4

Además, sólo por si acaso, asegúrese de restablecer el ExpiresDefaulten su .htaccessarchivo si usted está utilizando para permitir que el almacenamiento en caché.

ExpiresDefault "access plus 0 seconds"

Luego, puede usar ExpiresByTypepara establecer valores específicos para los archivos que desea almacenar en caché:

ExpiresByType image/x-icon "access plus 3 month"

Esto también puede ser útil si el navegador almacena en caché sus archivos dinámicos, como php, etc., y no puede entender por qué. Compruebe ExpiresDefault.


3

Además de los encabezados, considere publicar su página a través de https . Muchos navegadores no almacenarán en caché https de forma predeterminada.


3
//In .net MVC
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public ActionResult FareListInfo(long id)
{
}

// In .net webform
<%@ OutputCache NoStore="true" Duration="0" VaryByParam="*" %>

2

Para completar BalusC -> RESPUESTA Si está usando perl, puede usar CGI para agregar encabezados HTTP.

Usando Perl:

Use CGI;    
sub set_new_query() {
        binmode STDOUT, ":utf8";
        die if defined $query;
        $query = CGI->new();
        print $query->header(
                        -expires       => 'Sat, 26 Jul 1997 05:00:00 GMT',
                        -Pragma        => 'no-cache',
                        -Cache_Control => join(', ', qw(
                                            private
                                            no-cache
                                            no-store
                                            must-revalidate
                                            max-age=0
                                            pre-check=0
                                            post-check=0 
                                           ))
        );
    }

Usando apache httpd.conf

<FilesMatch "\.(html|htm|js|css|pl)$">
FileETag None
<ifModule mod_headers.c>
Header unset ETag
Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires "Wed, 11 Jan 1984 05:00:00 GMT"
</ifModule>

Nota: cuando traté de usar el html META, los navegadores los ignoraron y almacenaron en caché la página.


0

Solo quiero señalar que si alguien quiere evitar el almacenamiento en caché SOLO de contenido dinámico, agregar esos encabezados adicionales debe hacerse mediante programación.

Edité el archivo de configuración de mi proyecto para agregar encabezados sin caché, pero eso también deshabilitó el contenido estático de almacenamiento en caché, que generalmente no es deseable. La modificación de los encabezados de respuesta en el código garantiza que las imágenes y los archivos de estilo se almacenen en caché.

Esto es bastante obvio, pero aún vale la pena mencionarlo.

Y otra precaución. Tenga cuidado al usar el método ClearHeaders de la clase HttpResponse. Puede causar algunos moretones si lo usa imprudentemente. Al igual que me dio.

Después de redirigir en el evento ActionFilterAttribute, las consecuencias de borrar todos los encabezados están perdiendo todos los datos de sesión y datos en el almacenamiento TempData. Es más seguro redireccionar desde una Acción o no borrar los encabezados cuando se realiza la redirección.

Pensándolo bien, desaliento a todos a usar el método ClearHeaders. Es mejor eliminar los encabezados por separado. Y para configurar el encabezado Cache-Control correctamente, estoy usando este código:

filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
filterContext.HttpContext.Response.Cache.AppendCacheExtension("no-store, must-revalidate");

0

No tuve suerte con los <head><meta>elementos. Agregar parámetros relacionados con la caché HTTP directamente (fuera del documento HTML) realmente funciona para mí.

A continuación se muestra un código de muestra en Python usando web.headerllamadas web.py. Redacté mi código de utilidad personal irrelevante a propósito.

    importar web
    sistema de importación
    importar UTILIDADES PERSONALES

    myname = "main.py"

    urls = (
        '/', 'clase principal'
    )

    main = web.application (urls, globals ())

    render = web.template.render ("plantillas /", base = "diseño", caché = Falso)

    clase main_class (objeto):
        def GET (auto):
            web.header ("Cache-control", "no-cache, no-store, must-revalidate")
            web.header ("Pragma", "sin caché")
            web.header ("Caduca", "0")
            volver render.main_form ()

        POST def (auto):
            msg = "PUBLICADO:"
            form = web.input (function = None)
            web.header ("Cache-control", "no-cache, no-store, must-revalidate")
            web.header ("Pragma", "sin caché")
            web.header ("Caduca", "0")
            return render.index_laid_out (saludo = msg + form.function)

    if __name__ == "__main__":
        nargs = len (sys.argv)
        # Asegúrese de que haya suficientes argumentos después del nombre del programa python
        si nargs! = 2:
            LOG-AND-DIE ("% s: error de línea de comando, nargs =% s, debe ser 2", myname, nargs)
        # Asegúrese de que el número de puerto TCP sea numérico
        tratar:
            tcp_port = int (sys.argv [1])
        excepto Excepción como e:
            LOG-AND-DIE ("% s: tcp_port = int (% s) falló (no es un entero)", myname, sys.argv [1])
        # ¡Todo está bien!
        JUST-LOG ("% s: ejecutándose en el puerto% d", myname, tcp_port)
        web.httpserver.runsimple (main.wsgifunc (), ("localhost", tcp_port))
        main.run ()


¿No está esto cubierto muchas veces en las respuestas que han estado en el sitio durante años?
Martin Tournoij

Las directivas META funcionan en Internet Explorer y en las versiones de Edge 18 y anteriores. Los navegadores modernos no los admiten. crbug.com/2763
EricLaw

0

Vea este enlace a un Estudio de caso sobre el almacenamiento en caché:

http://securityevaluators.com/knowledge/case_studies/caching/

Resumen, según el artículo, solo Cache-Control: no-storefunciona en Chrome, Firefox e IE. IE acepta otros controles, pero Chrome y Firefox no. El enlace es una buena lectura completa con el historial de almacenamiento en caché y documentación de prueba de concepto.


0

No estoy seguro de si mi respuesta suena simple y estúpida, y tal vez ya lo ha sabido desde hace mucho tiempo, pero como evitar que alguien use el botón de retroceso del navegador para ver sus páginas históricas es uno de sus objetivos, puede usar:

window.location.replace("https://www.example.com/page-not-to-be-viewed-in-browser-history-back-button.html");

Por supuesto, es posible que esto no se pueda implementar en todo el sitio, pero al menos para algunas páginas críticas, puede hacerlo. Espero que esto ayude.


-1

puede usar el bloqueo de ubicación para establecer un archivo individual en lugar de que la aplicación completa almacene en caché en IIS

 <location path="index.html">
    <system.webServer>
      <httpProtocol>
        <customHeaders>
          <add name="Cache-Control" value="no-cache" />
        </customHeaders>
      </httpProtocol>
    </system.webServer>
  </location>
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.