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

PHP header() 圖片輸出與瀏覽器緩存控制:從驗證碼到動態圖片的完整實戰

     編輯:PHP 相關知識

核心痛點:動態圖片為何會重複下載?

使用 header('Content-Type: image/jpeg') 直接輸出 GD 生成的圖片(驗證碼、圖表、水印圖)時,若無緩存控制,瀏覽器每次請求都會重新下載,造成頻寬浪費與伺服器壓力。本文詳解 HTTP 緩存頭的正確配置,實現「首次下載、後續讀取緩存、內容更新自動失效」的完整流程。

關鍵 HTTP 頭詳細解析

Header作用範例值
Cache-Control控制緩存機制,private 僅客戶端緩存,max-age 秒數private, max-age=10800(3小時)
PragmaHTTP/1.0 相容性,private 同 Cache-Controlprivate
Expires絕對過期時間(RFC 822 格式)Wed, 21 Oct 2025 07:28:00 GMT
Last-Modified資源最後修改時間,配合 If-Modified-Since 實現 304gmdate('D, d M Y H:i:s', $mtime) . ' GMT'
ETag實體標籤(檔案雜湊/版本號),強驗證器md5_file($image_path)

完整實作:支援 304 Not Modified 的圖片輸出類

<?php
class ImageCacheResponse {
    private $imagePath;
    private $mimeType;
    
    public function __construct(string $imagePath) {
        $this->imagePath = $imagePath;
        $this->mimeType = $this->getMimeType();
    }
    
    public function send(): void {
        if (!file_exists($this->imagePath)) {
            http_response_code(404);
            exit('Image not found');
        }
        
        $mtime = filemtime($this->imagePath);
        $etag = md5_file($this->imagePath);
        $lastModified = gmdate('D, d M Y H:i:s', $mtime) . ' GMT';
        
        // 發送緩存控制頭
        header('Cache-Control: private, max-age=86400, must-revalidate'); // 1天
        header('Pragma: private');
        header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 86400) . ' GMT');
        header('Last-Modified: ' . $lastModified);
        header('ETag: "' . $etag . '"');
        header('Content-Type: ' . $this->mimeType);
        header('Content-Length: ' . filesize($this->imagePath));
        
        // 條件請求處理
        if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
            $clientTime = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
            if ($clientTime === $mtime) {
                header('HTTP/1.1 304 Not Modified');
                header('Last-Modified: ' . $lastModified);
                exit;
            }
        }
        if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
            $clientEtag = str_replace('"', '', $_SERVER['HTTP_IF_NONE_MATCH']);
            if ($clientEtag === $etag) {
                header('HTTP/1.1 304 Not Modified');
                header('ETag: "' . $etag . '"');
                exit;
            }
        }
        
        // 輸出圖片內容
        readfile($this->imagePath);
    }
    
    private function getMimeType(): string {
        $ext = strtolower(pathinfo($this->imagePath, PATHINFO_EXTENSION));
        return match($ext) {
            'jpg', 'jpeg' => 'image/jpeg',
            'png' => 'image/png',
            'gif' => 'image/gif',
            'webp' => 'image/webp',
            default => 'application/octet-stream'
        };
    }
}

// 使用範例
$image = new ImageCacheResponse(__DIR__ . '/captcha.jpg');
$image->send();

進階場景:動態生成圖片(縮圖/水印/驗證碼)

若圖片即時生成(無實體檔案),以內容雜湊作為 ETag,並儲存生成結果到快取目錄:

// 生成縮圖並快取
function outputThumbnail(string $src, int $maxW, int $maxH): void {
    $cacheDir = __DIR__ . '/cache/thumbs/';
    $cacheKey = md5($src . $maxW . $maxH . filemtime($src));
    $cacheFile = $cacheDir . $cacheKey . '.jpg';
    
    if (!file_exists($cacheFile)) {
        // 生成縮圖邏輯...
        imagejpeg($dst, $cacheFile, 90);
    }
    (new ImageCacheResponse($cacheFile))->send();
}

常見坑點與解決方案

  • Session 阻塞:session_start() 會發送 Cache-Control: no-cache,圖片輸出前務必 session_write_close() 或避開 Session
  • CDN 雙層緩存:源站設定 max-age=3600,CDN 設定 s-maxage=86400,利用 Cache-Control: private, s-maxage=86400 區分
  • 驗證碼不可緩存:header('Cache-Control: no-store, no-cache, must-revalidate') 強制每次重新生成
  • 中文檔名編碼:Content-Disposition: inline; filename*=UTF-8'' . rawurlencode($filename)

效能對比實測

策略首次請求二次請求(304)頻寬節省
無緩存頭200 OK / 45KB200 OK / 45KB0%
Expires + Last-Modified200 OK / 45KB304 / 0.2KB99.5%
ETag + Cache-Control200 OK / 45KB304 / 0.2KB99.5%

正確配置 HTTP 緩存,不僅節省頻寬,更能大幅降低伺服器 CPU/IO 壓力,是高效能 Web 應用的基礎功夫。

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