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

PHP 瀏覽器緩存機制深度解析:ETag、Last-Modified、Cache-Control 實戰配置

     編輯:PHP 相關知識

HTTP 緩存兩大模式

模式核心頭部流程適用場景
強緩存`Cache-Control`、`Expires`瀏覽器直接讀取本地副本,**不發請求**靜態資源(JS/CSS/圖片/字體)、版本化文件
協商緩存`Last-Modified`/`If-Modified-Since`、`ETag`/`If-None-Match`瀏覽器發請求詢問,**服務器返回 304 或 200**動態頁面、頻繁變更資源、需精確控制

關鍵頭部詳解

1. Cache-Control(現代首選)

Cache-Control: max-age=31536000, public, immutable  # 靜態資源一年,不可變
Cache-Control: max-age=300, must-revalidate         # 動態頁面 5 分鐘,過期必驗證
Cache-Control: no-cache                             # 每次請求必驗證(協商緩存)
Cache-Control: no-store                             # 完全禁止緩存(敏感數據)
Cache-Control: private, max-age=600                 # 僅瀏覽器緩存,CDN/代理不緩存
  • `max-age`:秒數,優先級高於 `Expires`。
  • `public`/`private`:是否允許中間代理緩存。
  • `must-revalidate`:過期後**必須**向源服務器驗證,不可使用過期副本。
  • `immutable`:資源永不變更(配合文件名哈希),瀏覽器甚至不發 304 請求。

2. ETag(實體標籤)

  • 強 ETag:`ETag: "abc123"` —— 字節級精確匹配,支援範圍請求。
  • 弱 ETag:`ETag: W/"abc123"` —— 語義等價(如壓縮前後)、不支援範圍請求。
  • 生成策略:`md5(file_content)`、`hash('crc32b', content)`、`版本號+修改時間`。

3. Last-Modified

  • 格式:`Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT`(HTTP-date)。
  • 精度 **秒級**,高併發下可能 1 秒內多次修改導致判斷失準,建議配合 ETag 使用。

PHP 實現協商緩存(動態頁面)

<?php
declare(strict_types=1);

function sendNotModified(array $validators = []): never
{
    // 發送 304,必須包含原有驗證頭
    header('HTTP/1.1 304 Not Modified');
    foreach ($validators as $h => $v) {
        header("$h: $v", true);
    }
    // 304 響應**不允許**有 Body
    exit;
}

function handleConditionalRequest(string $content, array $options = []): void
{
    $etag = $options['etag'] ?? md5($content);
    $lastModified = $options['last_modified'] ?? null; // Unix timestamp

    // 發送驗證頭
    header("ETag: \"$etag\"");
    if ($lastModified) {
        header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $lastModified) . ' GMT');
    }

    // 檢查 If-None-Match (ETag)
    $ifNoneMatch = $_SERVER['HTTP_IF_NONE_MATCH'] ?? '';
    if ($ifNoneMatch) {
        // 解析多個 ETag(逗號分隔)
        $clientEtags = array_map('trim', explode(',', $ifNoneMatch));
        if (in_array($etag, $clientEtags, true) || in_array('*', $clientEtags, true)) {
            sendNotModified(['ETag' => "\"$etag\""] + ($lastModified ? ['Last-Modified' => gmdate('D, d M Y H:i:s', $lastModified) . ' GMT'] : []));
        }
    }

    // 檢查 If-Modified-Since
    $ifModifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE'] ?? '';
    if ($lastModified && $ifModifiedSince) {
        $clientTime = strtotime($ifModifiedSince);
        if ($clientTime >= $lastModified) {
            sendNotModified(['Last-Modified' => gmdate('D, d M Y H:i:s', $lastModified) . ' GMT', 'ETag' => "\"$etag\""]);
        }
    }

    // 未命中緩存,發送 200 + 內容
    header('Content-Type: text/html; charset=utf-8');
    echo $content;
}

// 使用示例
$html = renderTemplate('article', ['id' => 123]);
$lastMod = $article->updated_at; // Unix timestamp
handleConditionalRequest($html, ['last_modified' => $lastMod]);

Nginx 靜態資源完美緩存配置

# 靜態資源(帶哈希文件名)
location ~* \.(?:css|js|png|jpg|jpeg|gif|webp|svg|woff2?|ico)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
    access_log off;
    add_header X-Cache-Status $upstream_cache_status;
}

# 動態頁面(PHP)
location ~ \.php$ {
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
    # 協商緩存由 PHP 控制,這裡只關閉強緩存
    add_header Cache-Control "no-cache, must-revalidate";
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

# HTML 頁面(可選:短期強緩存 + 協商)
location ~* \.html$ {
    expires 5m;
    add_header Cache-Control "public, must-revalidate";
    try_files $uri $uri/ /index.php$is_args$args;
}

Apache `.htaccess` 對應配置


    ExpiresActive On
    ExpiresByType text/css "access plus 1 year"
    ExpiresByType application/javascript "access plus 1 year"
    ExpiresByType image/png "access plus 1 year"
    ExpiresByType image/jpeg "access plus 1 year"
    ExpiresByType image/webp "access plus 1 year"
    ExpiresByType font/woff2 "access plus 1 year"
    ExpiresByType text/html "access plus 5 minutes"



    # 靜態資源 immutable
    
        Header set Cache-Control "public, max-age=31536000, immutable"
    
    # HTML 協商緩存
    
        Header set Cache-Control "public, max-age=300, must-revalidate"
    
    # 動態 API 禁止緩存
    
        Header set Cache-Control "no-cache, no-store, must-revalidate"
        Header set Pragma "no-cache"
        Header set Expires "0"
    

進階:Service Worker 離線緩存(PWA)

// sw.js
const CACHE_NAME = 'app-v1.2.3';
const STATIC_ASSETS = [
    '/', '/offline.html',
    '/assets/css/main.css',
    '/assets/js/app.js',
];

self.addEventListener('install', (e) => {
    e.waitUntil(caches.open(CACHE_NAME).then((cache) => cache.addAll(STATIC_ASSETS)));
    self.skipWaiting();
});

self.addEventListener('fetch', (e) => {
    // 策略:網絡優先,失敗回退緩存
    e.respondWith(
        fetch(e.request).catch(() => caches.match(e.request))
    );
});

self.addEventListener('activate', (e) => {
    e.waitUntil(
        caches.keys().then((keys) => Promise.all(
            keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k))
        ))
    );
    self.clients.claim();
});

調試與驗證清單

工具檢查點
Chrome DevTools Network查看 `Cache-Control`、`ETag`、`Last-Modified`、`Age`、`X-Cache-Status`;刷新觀察 `304` / `(from disk cache)` / `(from memory cache)`
curl -I`curl -I https://example.com/style.css` 驗證頭部
RedBot`https://redbot.org/` 自動分析緩存合規性
WebPageTest查看 `First Byte`、`Cache Static Content` 評分

常見誤區

  1. 動態頁面設置 `max-age` 長時間 → 用戶看到舊數據;正確:`no-cache, must-revalidate` + ETag/Last-Modified。
  2. 靜態資源無版本哈希卻用 `immutable` → 更新後瀏覽器永不重新獲取;正確:構建工具生成 `main.a1b2c3.css`。
  3. 同時發送 `ETag` 和 `Last-Modified` 但邏輯衝突** → HTTP 規範要求**兩者同時滿足**才返回 304,建議**優先 ETag**,Last-Modified 作備選。
  4. 304 響應帶 Body** → 違反 RFC 7232,導致瀏覽器解析錯誤;304 **絕對不能**有響應體。
  5. HTTPS 頁面引用 HTTP 靜態資源** → 混合內容阻攔,緩存失效;全站 HTTPS + 相對協議 `//cdn.example.com/...`。

總結

  • 靜態資源:文件名含哈希 + `Cache-Control: public, max-age=31536000, immutable` + CDN 緩存,**零請求**。
  • 動態頁面:`Cache-Control: no-cache, must-revalidate` + **ETag 優先** + Last-Modified 備選,**協商緩存**返回 304。
  • 敏感/個性化頁面:`Cache-Control: private, no-store` + `Vary: Cookie`,**完全不緩存**。
  • Nginx/Apache 靜態資源配置** 統一管理,**PHP 僅處理動態協商邏輯**。

正確配置 HTTP 緩存,能將靜態資源命中率提升至 99%+、動態頁面 304 比例超 80%,大幅降低帶寬成本與服務器壓力。

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