首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

深入分析 PHP 與瀏覽器緩存機制:ETag、Last-Modified 與 304 響應實戰

     編輯:PHP 綜合知識

瀏覽器緩存工作流程

  1. 首次請求:服務器返回 200 OK + 資源內容 + 緩存標識(Last-Modified、ETag) + 緩存策略(Cache-Control、Expires)。
  2. 再次請求:瀏覽器攜帶 If-Modified-Since(對應 Last-Modified)與 If-None-Match(對應 ETag)。
  3. 服務器比對:若資源未變 → 返回 304 Not Modified(無 Body),瀏覽器讀取本地緩存;若已變 → 返回 200 OK + 新內容 + 新標識。

關鍵 HTTP 標頭詳解

標頭方向作用
Last-ModifiedResponse資源最後修改時間(GMT)
If-Modified-SinceRequest詢問資源是否在該時間後修改
ETagResponse資源唯一指紋(如內容哈希、版本號)
If-None-MatchRequest攜帶上次收到的 ETag 進行比對
Cache-ControlResponse指定緩存策略(max-age、public、no-cache 等)
ExpiresResponse絕對過期時間(HTTP/1.0 兼容)
Pragma: no-cacheResponseHTTP/1.0 強制不緩存

PHP 實現:ETag + Last-Modified 雙重驗證

<?php
// 配置:客戶端緩存 1 小時
$cacheHours = 1;
$cacheSeconds = $cacheHours * 3600;
$etagValue = 'myapp_v1'; // 可根據內容哈希動態生成

// 發送緩存策略頭
header('Cache-Control: public, max-age=' . $cacheSeconds);
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $cacheSeconds) . ' GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Pragma: public');
header('ETag: ' . $etagValue);

// 1. 檢查 If-None-Match (ETag)
if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
    $clientEtag = trim($_SERVER['HTTP_IF_NONE_MATCH']);
    if ($clientEtag === $etagValue) {
        header('ETag: ' . $etagValue, true, 304);
        exit;
    }
}

// 2. 檢查 If-Modified-Since
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
    $clientTime = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
    // 允許緩存時間內視為未修改
    if (time() - $clientTime < $cacheSeconds) {
        header('ETag: ' . $etagValue, true, 304);
        exit;
    }
}

// 3. 正常輸出內容...
echo "頁面動態內容...";
?>

進階:動態生成 ETag

靜態 ETag 適用於整頁緩存。對於內容頻繁變化的頁面,應根據實際內容生成:

$content = generatePageContent(); // 你的業務邏輯
$etagValue = '"' . md5($content) . '"'; // 加雙引號符合 RFC 7232
header('ETag: ' . $etagValue);

// 輸出前再次檢查
if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] === $etagValue) {
    header('ETag: ' . $etagValue, true, 304);
    exit;
}
echo $content;

常見坑與注意事項

  • GZIP + ETag 衝突:Apache/Nginx 啟用 gzip 時可能修改 ETag(加 -gzip 後綴),導致 PHP 生成的 ETag 不匹配。解決:由 Web 服務器統一處理壓縮與 ETag,或 PHP 僅輸出未壓縮內容並禁用服務器端壓縮。
  • Last-Modified 精度僅至秒,高頻更新場景必須配合 ETag。
  • Cache-Control: no-cache 並非「不緩存」,而是「每次使用緩存前必須向服務器驗證」。真正禁止緩存用 no-store。
  • 對於私有數據(用戶專屬頁面),務必加 Cache-Control: private,防止代理服務器緩存洩露隱私。

總結

合理利用瀏覽器緩存可大幅降低服務器負載與頻寬。核心策略:強緩存控制頻率,協商緩存保證一致性。PHP 端只需正確發送標頭並處理 If-None-Match/If-Modified-Since 即可實現 304 響應。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved