🐡

HTTP Status Code 103 Early Hintsを試す

2023/07/09に公開

HTTP Status Code 103 Early Hints とはWebの読み込みを30%以上高速化させることが期待されている新しいWeb標準のステータスコードです。

OriginがHTTP ステータスコード200を戻すより先に、読み込むべきオブジェクト(cssや画像など)をHTTP ステータスコード 103で戻しブラウザは先に読み込みを開始させます。つまりCDN側のキャッシュ読み込みを、オリジンからCDNにHTTP200が戻るより先に開始させます。

本来これを実装させるためには、オリジンがHTTP ステータスコード103に対応している必要がありますが、Cloudflareの Early Hints機能をオンにすることでHTTPレスポンスヘッダのlinkに設定されているpreloadもしくはpreconnectionを読み取りクライアントブラウザにHTTP103を戻します。
以下の例であれば、クライアントは先にオリジンからCDN、CDNからブラウザにHTTP200が戻るより前にa.pngの読み込みを開始させます。

HTTP/2 103 
link: <a.png>; as=image; rel=preload

HTTP/2 200
date: Sun, 09 Jul 2023 11:53:46 GMT
content-type: text/html
last-modified: Sun, 09 Jul 2023 11:42:05 GMT
link: : <a.png>; rel=preload; as=image
accept-ranges: bytes
cf-cache-status: DYNAMIC
server: cloudflare
cf-ray: 7e405e50edde2647-NRT

詳しいフローはこの図を参考にしてください。このブログ記事でその動作がよくまとまっています。

CloudflareではオリジンがHTTP103を戻さなくても、HTTPレスポンスヘッダに含まれているlink: : <a.png>; rel=preload; as=imageを読み取り、先行してHTTP 103としてそのlink情報、つまりオブジェクトの読み込み先情報を先に戻します。

なおこの機能はHTTP/2移行で動作し、HTTP/1.1では未サポートです。また現在Chrome、Edge、Firefoxがその対応を表明しているものの、ブラウザにはまだ標準実装されておらず、今後数年間かけてスタンダードになると思われます。

やってみる

テストを行うためのオリジンを作ります。Cloudflare Pagesが便利なのですが、このEarly HintsはHTTP200を戻した方が処理が速いと判断される場合は、103ではなく200を戻す性質があり、PagesではHTTP103を再現させることができませんでした。(恐らくCloudflare CDNのキャッシュとオリジンが同じ場所にあることに起因していると予想しますが詳細は不明です・・・すいません)
と、いうことでEC2にnginxをインストールして使います。

 sudo dnf install nginx

インストールが終わったらnginxを起動します。

sudo service nginx start

一度デフォルトでアクセスできるか確認しておきます。

sudo service nginx start

cd /usr/share/nginx/html

を実行しhtmlを作成します。

index.htmlは以下に置き換えます。a.pngは何でも良いので同じディレクトリにおいておきます。

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <title>Hello World</title>
  <link rel="stylesheet" href="style.css">
  <link rel="preload" href="a.png">
</head>

<body>
  <h1>Hello World</h1>
  <p>こんにちは、世界!</p>
  <img src="a.png"/>
</body>

</html>

ちょうどいい画像が見当たらない場合以下を試してみて下さい

sudo wget https://p.e-words.jp/img/PNG.png
sudo cp PNG.png a.png

nginxのnginx.confを以下のように修正します。`/etc/nginxに存在します

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
worker_connections 1024;
}

http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

sendfile on;
tcp_nopush on;
keepalive_timeout 65;
types_hash_max_size 4096;
include /etc/nginx/mime.types;
default_type application/octet-stream;

# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
include /etc/nginx/conf.d/*.conf;

server {
listen 80;
listen [::]:80;
server_name _;
root /usr/share/nginx/html;
#Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
error_page 404 /404.html;
location = /404.html {
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
}

location / {
add_header Link: "<a.png>; rel=preload; as=image";
  }
}
location / {
add_header Link: "<a.png>; rel=preload; as=image";
  }

部分が追記部分です。これによりHTTPレスポンスヘッダにlink: : <a.png>; rel=preload; as=imageが戻るようになり、CloudflareはこれをもとにHTTP103をレスポンスで出力します。
では一度nginxを再起動します

sudo service nginx restart
curl.exe -k -X HEAD -I https:<origin domain>

でアクセスすると以下のようなレスポンスが戻ります。

HTTP/2 200
date: Sun, 09 Jul 2023 12:07:35 GMT
content-type: text/html
last-modified: Sun, 09 Jul 2023 11:42:05 GMT
link: : <a.png>; rel=preload; as=image
accept-ranges: bytes
cf-cache-status: DYNAMIC
server: cloudflare
cf-ray: 7e4072913b27e035-NRT
link: : <a.png>; rel=preload; as=image

が出力されていれば成功です。

DNS Proxyモード設定

ここまでできたらいつも通りDNS ProxyモードをCloudflareで設定します。これによりCloudflare CDN機能が利用可能となります。
ドメイン用管理者画面のSpeedから最適化をクリックします。

コンテンツの最適化タブでEarly Hintsをオンにします。

以上で設定は完了です。

curl.exe -k -X HEAD -I https:<cloudflare domain>

でアクセスを行うと以下のようなレスポンスとなります。

HTTP/2 103 
link: <a.png>; as=image; rel=preload

HTTP/2 200
date: Sun, 09 Jul 2023 12:07:35 GMT
content-type: text/html
last-modified: Sun, 09 Jul 2023 11:42:05 GMT
link: : <a.png>; rel=preload; as=image
accept-ranges: bytes
cf-cache-status: DYNAMIC
server: cloudflare
cf-ray: 7e4072913b27e035-NRT

HTTP200より先にHTTP103として画像のパスが戻ってきていることがわかります。
期待通りの動作をしない場合、以下を行ってみて下さい。

  1. curlを最新版にする。(私の環境では8.0.1では動作せず、8.1.2で動作しました)
  2. Cloudflareのキャッシュをパージ(削除)し、数回アクセスを行う。
  3. a.pngを大きい画像に変更して何度か試す
    HTTP200を戻してしまった方が早い場合(テスト環境に依存しますがファイルが小さい場合)は103より200が優先されます。

いかがでしょうか。冒頭記載した通り、Chrome、Edge、Firefoxが対応を表明していることもあり、今後SEO観点で重要な機能となるはずです。ぜひ試してみて下さい。

余談

どうしても動作しなかったので、動作に成功した同僚の@yusukebeさんに動いている環境を見せてもらいました。Cloudflare documentでは

link: : a.png; rel=preload; as=image

としてHTTPレスポンスヘッダを戻すようになっていましたが、正しくは <> 付きの以下でした。

link: : <a.png>; rel=preload; as=image

いやー、持つべきものは優秀な同僚ですね(笑) ありがとうございました!

Discussion