PHP如何实现过期缓存与内容更新触发的智能刷新缓存,及缓存头设置失效问题解决
如何在PHP中实现带过期机制且支持内容更新自动刷新的缓存
一、先解决你当前HTTP缓存失效的问题
你现在的代码只设置了过期头,但没处理浏览器的If-Modified-Since请求头,导致每次浏览器都会重新请求完整内容,而不是复用本地缓存。要让HTTP缓存正常工作,服务器端得验证资源是否已修改,返回304状态码让浏览器直接用缓存。
修正后的代码示例:
<?php $seconds_to_cache = 300; // 用内容的最后修改时间(如果是动态内容,换成数据库内容的更新时间) $last_modified_time = filemtime(__FILE__); // 用内容哈希作为ETag,更精准标识资源版本 $etag = md5_file(__FILE__); // 发送资源版本标识头 header("Last-Modified: " . gmdate("D, d M Y H:i:s", $last_modified_time) . " GMT"); header("ETag: $etag"); // 检查浏览器携带的缓存验证信息 if (@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) === $last_modified_time || trim($_SERVER['HTTP_IF_NONE_MATCH']) === $etag) { // 资源未修改,返回304让浏览器用缓存 header("HTTP/1.1 304 Not Modified"); exit; } // 设置缓存过期规则 header("Expires: " . gmdate("D, d M Y H:i:s", time() + $seconds_to_cache) . " GMT"); header("Cache-Control: max-age=$seconds_to_cache, public"); header("Pragma: cache"); // 这里是你的页面内容输出逻辑 echo "当前时间:" . date('Y-m-d H:i:s'); ?>
简单解释下:
Last-Modified和ETag是给浏览器的资源版本标记,后续请求浏览器会带上这些信息- 服务器对比这些标记,确认资源没变化就返回304,浏览器直接用本地缓存,不用重新下载内容
- 如果是动态生成的内容(比如从数据库取数据),把
filemtime(__FILE__)换成内容的最后更新时间,md5_file(__FILE__)换成内容的哈希值就行
二、实现内容更新时自动刷新的智能缓存
HTTP缓存适合静态或低频变化的内容,但要做到内容一更新就自动刷新缓存,更靠谱的是用服务器端缓存(文件缓存、Redis都可以),核心思路是给缓存绑定一个「版本标识」,内容更新时版本标识变化,缓存自动失效。
方案1:文件缓存(适合小型站点)
<?php // 缓存文件存储路径 $cache_file = './cache/home_page.cache'; // 内容版本标识:比如从数据库取最新内容的更新时间 $content_version = get_content_last_update_time(); // 检查缓存是否有效 if (file_exists($cache_file)) { $cache_data = unserialize(file_get_contents($cache_file)); // 版本一致+未过期,直接输出缓存 if ($cache_data['version'] === $content_version && time() < $cache_data['expire_time']) { echo $cache_data['content']; exit; } } // 缓存无效,生成最新页面内容 $page_content = generate_page_content(); // 保存新缓存,同时记录版本和过期时间 $cache_data = [ 'version' => $content_version, 'content' => $page_content, 'expire_time' => time() + 300 // 双重保险:即使版本标识没更新,缓存也会过期 ]; file_put_contents($cache_file, serialize($cache_data)); // 输出内容 echo $page_content; // 辅助函数示例:获取内容最后更新时间 function get_content_last_update_time() { $pdo = new PDO('mysql:host=localhost;dbname=your_db', 'user', 'password'); // 假设你有articles表,取最新修改的文章时间 $stmt = $pdo->query("SELECT MAX(updated_at) FROM articles"); return $stmt->fetchColumn(); } // 辅助函数示例:生成页面内容 function generate_page_content() { // 这里写你的页面渲染逻辑,比如从数据库取数据生成HTML return "<h1>最新文章列表</h1>" . date('Y-m-d H:i:s'); } ?>
这个方案的好处是:只要内容更新(比如数据库里的文章被修改),$content_version就会变化,缓存直接失效,下次请求自动生成新缓存,同时还保留了过期时间,避免版本标识更新失败导致缓存永久有效。
方案2:Redis缓存(适合高并发站点)
如果你的网站流量较大,用Redis这类内存缓存效率更高,思路和文件缓存一致,只是把缓存存在Redis里:
<?php $redis = new Redis(); $redis->connect('localhost', 6379); $cache_key = 'cache:home_page'; $content_version = get_content_last_update_time(); $cache_data = $redis->get($cache_key); if ($cache_data) { $cache_data = json_decode($cache_data, true); if ($cache_data['version'] === $content_version) { echo $cache_data['content']; exit; } } // 生成最新内容 $page_content = generate_page_content(); // 保存到Redis,同时设置过期时间 $redis->set($cache_key, json_encode([ 'version' => $content_version, 'content' => $page_content ])); $redis->expire($cache_key, 300); echo $page_content; ?>
三、额外注意事项
- 如果页面有用户个性化内容(比如登录用户的专属信息),要给缓存key加上用户ID,避免不同用户看到相同的缓存内容
- 开发环境可以临时关闭缓存:
header("Cache-Control: no-cache, no-store, must-revalidate");,方便调试 - 对于数据库驱动的内容,记得在修改、添加数据后,确保
get_content_last_update_time能返回最新的时间戳
内容的提问来源于stack exchange,提问作者Sash




