Caché del navegador
Guardar en caché
A menudo se emplea PHP simplemente para añadir menús, encabezados o pies de página usando el tradicional include(). Un efecto secundario es que al tratarse de un script en PHP la página ya no se guarda en la caché del navegador y se descarga desde Internet cada vez que se visualiza; sieeeempre la misma página porque nada cambia en ella de una vez a otra.
Con esta función, llamada al principio de nuestro script, le indicamos al navegador la fecha real de la última modificación y cuánto tiempo debe conservar la página en caché.
/**
* Genera los encabezados necesarios para indicar que el documento se guarde o no en caché
*
* $activar (bool): genera encabezados para guardar en caché (TRUE) o para no cachear (FALSE)
* $horas (entero): tiempo que el navegador debe conservar la página en caché
* $if_modified_since (bool): indica si respetar el encabezado If-Modified-Since
*
* Si $if_modified_since es TRUE, cuando la página no se ha modificado desde la fecha indicada en
* If-Modified-Since se genera un código de estado HTTP 304 y se detiene la ejecución del script
*
* Nota: esta función utiliza la fecha de modificación de la _página actual_ como referencia. Esta fecha
* no tiene en cuenta otros archivos (como bloques situados en includes) o contenidos dinámicos por lo que
* sólo es una medida fiable en páginas estáticas o pseudo-estáticas. Esto es un problema si se utiliza
* $if_modified_since=TRUE en páginas dinámicas ya que el navedor podría conservar una versión obsoleta
* mucho más allá de lo establecido en $horas
*/
function cache($activar, $horas=3, $if_modified_since=FALSE){ //v2011-02-11
// Obecedemos If-Modified-Since si queremos usar caché
if( $activar && $if_modified_since && isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ){
$fecha_cache = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
if( $fecha_cache!==FALSE && $fecha_cache>=getlastmod() ){
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', getlastmod()) . ' GMT', TRUE, 304);
exit;
}
}
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', getlastmod()) . ' GMT');
if($activar){
// Guardar en caché
header('Expires: ' . gmdate('D, d M Y H:i:s', time()+60*60*$horas) . ' GMT');
header('Cache-Control: max-age=' . 60*60*$horas . ', s-maxage=' . 60*60*$horas . ', must-revalidate, proxy-revalidate');
session_cache_limiter(FALSE); // Deshabilitamos los encabezados de caché de session_start()
if( session_id() ){
// Una sesión ya iniciada ha generado un encabezado Pragma: no-cache
if( function_exists('header_remove') ){
header_remove('Pragma');
}else{
header('Pragma:');
}
}
}else{
// No guardar en caché
header('Expires: ' . gmdate('D, d M Y H:i:s', time()-86400*365*10) . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
header('Pragma: no-cache');
}
}
Nótese que esto no nos impide recargar la página con el botón de actualizar y, en cambio, hace mucho más ágil navegar por el sitio.
No guardar en caché
Si no queremos reutilizar la página de la caché podemos llamar a la función con $activar=FALSE. O podemos simplemente no llamarla: ésa acostumbra a ser configuración predeterminada de PHP.
Notas
- La función genera encabezados HTTP; hay que llamarla antes de escribir nada en pantalla.
- Es interesante ajustar el valor predeterminado de
$horas en la definición de la función y ejecutar simplemente cache(TRUE).
- La respuesta a
If-Modified-Since mira únicamente la fecha del documento principal. Si sólo ha cambiado algún archivo insertado vía include no se detectará. Esto puede ser bug o feature.
Cache-Control y Pragma son directivas para uso de navegadores y proxies, pero antes que contar alguna burrada les invito a buscar en Google.
- Algunas extensiones de Firefox (como Firebug) parecen interferir en la caché de las páginas sobre las que se utilizan. Conviene tenerlo en cuenta antes de darse cabezados contra la pared.
Acerca de If-Modified-Since
Este encabezado HTTP, enviado por el navegador, permite mantener una conversación tal que así:
- Navegador: Ya vi esta página el martes pasado. Mándamela sólo si ha cambiado.
- Servidor: Entonces no te mando nada. Hace un mes que no se toca.
Al contrario que con el sistema «ten esto tres horitas en caché y luego me lo vuelves a pedir», el navegador conservará la versión que tenga hasta que le digamos que la página ha cambiado. Y si la página cambia sin que nos demos cuenta esto pueden ser semanas o meses. Para que el sistema sea funcional necesitamos bien que la página sea estática (en cuyo caso getlastmod() nos da la información correcta) bien indicar a mano la fecha de modificación (sería fácil retocar la función para alimentarla con una fecha sacada de, por ejemplo, la base de datos). Si no se está seguro es mejor dejar $if_modified_since=FALSE.
Historial
- v2011-02-11
- Documentar mejor los parámetros y cambiar el valor predeterminado de
$if_modified_since.
- v2010-08-23
- Implementar la respuesta al encabezado
If-Modified-Since.
- La caché no se activaba en páginas con sesiones si la sesión había sido iniciada con anterioridad.
- Generar el encabezado
Last-Modified también al desactivar la caché.
- v2009-05-08
- Agrupado el código en una función y actualizadas las explicaciones.
- La caché no se activaba en páginas con sesiones.
- v2005-08-25
- Primera versión.