為你的BackEnd加上 HTTP 304

tags: Web HTTP
category: Back-End
description: 為你的BackEnd加上 HTTP 304
created_at: 2022/04/28 20:30:00

cover image


前言

最近看到這個狀態碼,想說3開頭常用的就是 301 與 302,都是轉址相關的,結果還有這個,就稍微嘗試了一下。

為了比較簡單呈現,這邊使用 PHP 當作範例。


使用流程

  1. 後端回應資料時多帶一個 EtagHeader
  2. 瀏覽器會幫你記錄下 Etag
  3. 之後再發送 Request 時瀏覽器會在 Header 幫你帶入 Etag 的值 (但是 KeyIf-None-Match)
  4. 後端再去檢查 If-None-Match 是否等於 Etag,如果相等就將狀態碼改為 304 ,這樣就不會重新傳輸一樣的資料

Etag 是一個將你的 Response 經過你自己定義的 Hash 之後的結果。

另外後端也可以額外帶入 Last-ModifiedHeader,這樣之後瀏覽器在發請求也會幫你帶入一個 If-Modified-SinceHeader,讓你去檢查這個資料需不需要被重新回傳,如果不需要重新回傳則設定狀態碼為 304 。

Header Key 對應關係

Response Request
Etag If-None-Match
Last-Modified If-Modified-Since

先做個304的實驗

這邊先寫一個 PHP 檔案,然後簡單回應資料

<?php
    echo 123;

這時候先去瀏覽器打開你的網頁,之後將程式加上設定狀態碼 304 並修改輸出值

<?php
    http_response_code(304);
    echo 12345;

這時候去重新整理你的網頁,會發現它不更新。


加入 Header

加入 Header 之前,可以先去 開發人員工具(F12) ,看一下回傳的 Header,應該滿乾淨的(?)

先加入 Etag

<?php
    header('Etag: 100');
    echo 12345;

重新整理網頁之後,會看到 Response Header 多了一條 Etag: 100,而這時你的 Request Header 應該還沒出現 If-None-Match: 100 這個東西,但是當你再次重新整理,他就會出現了。


判斷 Etag 決定要不要回傳 304

這邊假設單純使用 md5 將回應的資料做 Hash 當作 Etag

<?php
    $response_data = "hi hi";
    $etag = md5($response_data);
    header("Etag: {$etag}");
    echo $response_data;

這時你會看到 Etag = 88a44c9ed9d268808439e4fb4de9a065,那麼再來要看怎麼抓到 If-None-Match 的值。

PHP 有 2 種方式

$method1 = apache_request_headers()['If-None-Match'] ?? '';
$method2 = $_SERVER['HTTP_IF_NONE_MATCH'] ?? '';

這兩個方式都可以抓到 If-None-Match 值,接下來都用方法二當作範例(畢竟方法一是先抓出所有 headers 在從之中抓一個出來)

<?php
    $response_data = "hi hi";
    $etag = md5($response_data);
    header("Etag: {$etag}");

    if ($etag === $_SERVER['HTTP_IF_NONE_MATCH']) {
        http_response_code(304);
    }

    echo $response_data;

這樣的話如果 $response_data 沒改變的情況下,就不會重新回傳資料。

然後這邊建議在確定回傳 304 時還是做一下 return 來結束程式執行,原因是這個樣子的:

看一下下面這一段程式

這樣子瀏覽器會不會多等那段時間呢? (即使已經確定是304)

<?php
    $response_data = "hi hi";
    $etag = md5($response_data);
    header("Etag: {$etag}");

    if ($etag === $_SERVER['HTTP_IF_NONE_MATCH']) {
        http_response_code(304);
    }

    sleep(3); // 假設這邊不知道誰弄了一個跑很久的東西

    echo $response_data;

他還是會等待的,所以只要改成這樣就沒事了

<?php
    $response_data = "hi hi";
    $etag = md5($response_data);
    header("Etag: {$etag}");

    if ($etag === $_SERVER['HTTP_IF_NONE_MATCH']) {
        http_response_code(304);
        return;
    }

    sleep(3);

    echo $response_data;

至於 Last-ModifiedIf-Modified-Since 就自己玩玩看吧(?),用法跟上面差不多。

主要是有時候可能需要檢查的不只是資料有沒有改變,也要檢查原本瀏覽器記住的值是不是過舊,需要更新。




最後更新時間: 2022年04月28日.