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

PHP 輸出靜態資源時實現 304 Not Modified 瀏覽器快取策略

     編輯:PHP 綜合知識

問題背景

當圖片、CSS、JS 等靜態資源由 PHP 動態輸出(如權限控管、水印、統計)時,若無快取機制,瀏覽器每次請求都會收到 200 OK 並重複下載,浪費頻寬與伺服器資源。正確的做法是利用 Last-Modified 與 ETag 回應標頭,配合請求標頭 If-Modified-Since、If-None-Match 實現 304 Not Modified。

核心代碼:可復用的 _addEtag() 方法

<?php
private function _addEtag($file) {
    $lastModified = filemtime($file);               // 檔案最後修改時間(Unix 時間戳)
    $etag = md5_file($file);                        // 檔案內容 MD5 作為 ETag(強驗證器)

    // 1. 總是發送快取驗證標頭
    header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $lastModified) . ' GMT');
    header('ETag: ' . $etag);

    // 2. 檢查瀏覽器攜帶的條件請求標頭
    $ifModifiedSince = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) 
                       ? strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) : 0;
    $ifNoneMatch = isset($_SERVER['HTTP_IF_NONE_MATCH']) 
                   ? trim($_SERVER['HTTP_IF_NONE_MATCH']) : '';

    // 3. 滿足任一條件即回傳 304
    if ($ifModifiedSince === $lastModified || $ifNoneMatch === $etag) {
        header('HTTP/1.1 304 Not Modified');
        exit; // 終止腳本,不輸出實體內容
    }
}

使用方式

<?php
// 輸出圖片前呼叫
$imagePath = '/path/to/photo.jpg';
_addEtag($imagePath);

// 發送內容型別與實體
header('Content-Type: image/jpeg');
readfile($imagePath); // 或 imagejpeg($gdResource)

關鍵細節解析

  • ETag 選擇:md5_file() 針對內容計算雜湊,確保內容變更必然導致 ETag 改變(強驗證)。若效能敏感,可改用 spl_file_info::getSize() . '-' . $lastModified(弱驗證)。
  • 日期格式:gmdate('D, d M Y H:i:s') 產生 RFC 7231 規範的 HTTP 日期(GMT)。
  • 條件判斷順序:RFC 7232 建議 If-None-Match 優先於 If-Modified-Since,但兩者均滿足即可回 304。
  • 安全模式下的 strtotime():加 @ 抑制警告,避免惡意標頭導致錯誤日誌污染。

進階:加入 Cache-Control 與 Expires

// 公開快取 1 小時,瀏覽器可直接使用快取而不發請求
header('Cache-Control: public, max-age=3600');
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 3600) . ' GMT');

配合 304 機制:首次請求 200 + 快取標頭 → 1 小時內直接讀快取 → 過期後發條件請求 → 304 或 200。

總結

僅需數行代碼即可為 PHP 動態輸出的靜態資源加入專業級快取驗證,大幅降低頻寬與延遲。記得在輸出實體之前呼叫 _addEtag(),並確保檔案路徑真實存在。

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