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

前言
最近看到這個狀態碼,想說3開頭常用的就是 301 與 302,都是轉址相關的,結果還有這個,就稍微嘗試了一下。
為了比較簡單呈現,這邊使用 PHP 當作範例。
使用流程
- 後端回應資料時多帶一個
Etag的Header - 瀏覽器會幫你記錄下
Etag - 之後再發送
Request時瀏覽器會在Header幫你帶入Etag的值 (但是Key為If-None-Match) - 後端再去檢查
If-None-Match是否等於Etag,如果相等就將狀態碼改為 304 ,這樣就不會重新傳輸一樣的資料
Etag 是一個將你的 Response 經過你自己定義的 Hash 之後的結果。
另外後端也可以額外帶入 Last-Modified 的 Header,這樣之後瀏覽器在發請求也會幫你帶入一個 If-Modified-Since 的 Header,讓你去檢查這個資料需不需要被重新回傳,如果不需要重新回傳則設定狀態碼為 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-Modified 與 If-Modified-Since 就自己玩玩看吧(?),用法跟上面差不多。
主要是有時候可能需要檢查的不只是資料有沒有改變,也要檢查原本瀏覽器記住的值是不是過舊,需要更新。