デフォルトでより鮮度の高い Service Worker

tl;dr

Chrome 68 以降、Service Worker スクリプトの更新を確認する HTTP リクエストは、デフォルトで HTTP キャッシュで実行されなくなります。これにより、Service Worker スクリプトに誤って Cache-Control ヘッダーを設定すると、更新の遅延につながるよくあるデベロッパーの問題を回避できます。

Cache-Control: max-age=0 を指定して /service-worker.js スクリプトの HTTP キャッシュをすでにオプトアウトしている場合は、新しいデフォルトの動作による変更はありません。

また、Chrome 78 以降では、importScripts() を介して Service Worker に読み込まれたスクリプトにバイト単位の比較が適用されます。インポートしたスクリプトが変更されると、最上位の Service Worker に対する変更と同様に、Service Worker の更新フローがトリガーされます。

背景情報

Service Worker のスコープ内の新しいページに移動したり、JavaScript から registration.update() を明示的に呼び出したり、push イベントや sync イベントを介して Service Worker が「起動」されたりするたびに、ブラウザは、最初に navigator.serviceWorker.register() 呼び出しに渡された JavaScript リソースをリクエストして、Service Worker スクリプトの更新を探します。

この記事では、その URL が /service-worker.js であり、Service Worker 内で実行される追加コードを読み込む importScripts() への 1 つの呼び出しが含まれているとします。

// Inside our /service-worker.js file:
importScripts('path/to/import.js');

// Other top-level code goes here.

変更内容

Chrome 68 より前では、/service-worker.js の更新リクエストは HTTP キャッシュ経由で行われていました(ほとんどの取得と同様)。つまり、スクリプトが最初に Cache-Control: max-age=600 で送信された場合、次の 600 秒(10 分)以内の更新はネットワークに送信されないため、ユーザーは最新バージョンの Service Worker を受け取れない可能性があります。ただし、max-age が 86,400(24 時間)より大きい場合は、ユーザーが特定のバージョンで永久に停止しないように、86,400 のように扱われます。

68 以降では、Service Worker スクリプトの更新をリクエストするときに HTTP キャッシュが無視されるため、既存のウェブ アプリケーションでは、Service Worker スクリプトのリクエスト頻度が増加する可能性があります。importScripts のリクエストは、引き続き HTTP キャッシュ経由で送信されます。ただし、これはデフォルトです。新しい登録オプション updateViaCache を使用して、この動作を制御できます。

updateViaCache

デベロッパーは、navigator.serviceWorker.register() を呼び出すときに新しいオプション updateViaCache パラメータを渡すことができるようになりました。'imports''all''none' の 3 つの値のいずれかを取ります。

これらの値により、更新された Service Worker リソースを確認する HTTP リクエストの送信時に、ブラウザの標準 HTTP キャッシュが使用されるかどうか、またどのように使用されるかが決まります。

  • 'imports' に設定すると、/service-worker.js スクリプトの更新を確認するときに HTTP キャッシュは参照されませんが、インポートされたスクリプト(この例では path/to/import.js)を取得するときに参照されます。これはデフォルトであり、Chrome 68 以降の動作と同じです。

  • 'all' に設定すると、最上位の /service-worker.js スクリプトと、Service Worker 内にインポートされたスクリプト(path/to/import.js など)の両方をリクエストするときに、HTTP キャッシュが参照されます。このオプションは、Chrome 68 より前の Chrome の動作に対応しています。

  • 'none' に設定すると、最上位の /service-worker.js またはインポートされたスクリプト(仮の path/to/import.js など)のリクエストを行う場合、HTTP キャッシュは参照されません。

たとえば、次のコードは、Service Worker を登録し、/service-worker.js スクリプト、または /service-worker.js 内の importScripts() を介して参照されるスクリプトの更新を確認するときに HTTP キャッシュを参照しないようにします。

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js', {
    updateViaCache: 'none',
    // Optionally, set 'scope' here, if needed.
  });
}

インポートしたスクリプトの更新を確認する

Chrome 78 より前では、importScripts() を介して読み込まれた Service Worker スクリプトは 1 回のみ取得されていました(updateViaCache 構成に応じて、最初に HTTP キャッシュ、またはネットワーク経由で確認)。最初の取得の後は、ブラウザによって内部的に保存されます。再度取得されることはありません。

すでにインストールされている Service Worker にインポートされたスクリプトの変更を反映させる唯一の方法は、スクリプトの URL を変更することです。通常は semver 値importScripts('https://example.com/v1.1.0/index.js') など)を追加するか、コンテンツのハッシュ(importScripts('https://example.com/index.abcd1234.js') など)を追加します。インポートされた URL を変更することによる副作用は、最上位の Service Worker スクリプトのコンテンツ更新フローがトリガーされ、それによって Service Worker のフローがトリガーされます。

Chrome 78 以降では、最上位の Service Worker ファイルに対して更新チェックが実行されるたびに、インポートされたスクリプトの内容が変更されていないかどうかのチェックが同時に行われます。使用される Cache-Control ヘッダーによっては、updateViaCache'all' または 'imports'(デフォルト値)に設定されている場合、インポートされたスクリプトのチェックが HTTP キャッシュによって実行される場合もあれば、updateViaCache'none' に設定されている場合はネットワークに対して直接実行される場合もあります。

インポートされたスクリプトの更新チェックで、以前に Service Worker によって保存された内容とバイト単位での差異が生じる場合は、最上位の Service Worker ファイルが同じままであっても、Service Worker の完全な更新フローがトリガーされます。

Chrome 78 の動作は、数年前の Firefox 56 で実装された動作と一致します。Safari にも、すでにこの動作が実装されています。

デベロッパーは何をする必要がありますか?

Cache-Control: max-age=0(または同様の値)を指定して /service-worker.js スクリプトの HTTP キャッシュを効果的に無効にしている場合、新しいデフォルトの動作による変化はありません。

意図的に、または単にホスティング環境のデフォルトとして HTTP キャッシュを有効にして /service-worker.js スクリプトを提供すると、サーバーに対して /service-worker.js に対する追加の HTTP リクエスト(以前は HTTP キャッシュで処理されていたリクエスト)が増加する可能性があります。引き続き Cache-Control ヘッダー値が /service-worker.js の鮮度に影響を与えるようにするには、Service Worker の登録時に updateViaCache: 'all' を明示的に設定する必要があります。

古いバージョンのブラウザを使用するユーザーのロングテールを考慮すると、新しいブラウザでは無視される可能性がありますが、Service Worker スクリプトには Cache-Control: max-age=0 HTTP ヘッダーの設定を継続することをおすすめします。

デベロッパーはこの機会を利用して、インポートしたスクリプトを HTTP キャッシュから明示的にオプトアウトするかどうかを決定し、必要に応じて updateViaCache: 'none' を Service Worker 登録に追加できます。

インポートしたスクリプトの提供

Chrome 78 以降では、importScripts() で読み込まれたリソースに対する HTTP 受信リクエストの数が増える可能性があります。これは、これらのリソースに対して更新がチェックされるようになったためです。

この追加の HTTP トラフィックを回避するには、URL に semver またはハッシュを含むスクリプトを提供するときに有効期間の長い Cache-Control ヘッダーを設定し、'imports' のデフォルトの updateViaCache 動作を使用します。

また、インポートしたスクリプトに頻繁な更新がないか確認する場合は、Cache-Control: max-age=0 または updateViaCache: 'none' を使用して提供します。

関連情報

いずれも Jake Archibald による「The Service Worker Lifecycle」と「Caching best practices & max-age をオン」は、ウェブに何かをデプロイするすべてのデベロッパーにおすすめです。