為你的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
就自己玩玩看吧(?),用法跟上面差不多。
主要是有時候可能需要檢查的不只是資料有沒有改變,也要檢查原本瀏覽器記住的值是不是過舊,需要更新。