IndexedDBにおけるストレージの永続化(Persistent Storage)の基礎と活用
はじめに
Webアプリケーションでは、オフライン機能やPWA (Progressive Web App) の実現のために大量のデータをブラウザに保存することがあります。その際によく使われる仕組みが IndexedDB
です。しかし、ブラウザのストレージ容量が逼迫すると、ブラウザはディスク容量を確保するために使用されていないサイトの保存データを自動的に削除(エビクション: eviction)することがあります。例えばオフラインアプリでサーバと同期されていないデータがある場合、これが削除されてしまうとユーザーデータが失われ、アプリの信頼性に悪影響を及ぼします。こうしたデータ消失のリスクに対処するため、ブラウザは開発者が任意で選択できる Persistent Storage
(永続ストレージ) モードを提供しています。Persistent Storage
を利用すると、ストレージが圧迫された状況でもブラウザがそのサイトのデータを勝手に削除しなくなり、ユーザー自身が明示的に消さない限りデータが保持されます。実際には、Chromeチームの調査によればストレージが自動的に消去されるケースは稀で、多くの場合ユーザー自身が手動でデータを消去しています。しかし、アプリにとって特に重要なデータを確実に保持するにはPersistent Storage
の活用が有効です。本記事では、IndexedDB
におけるデフォルトの Temporary Storage
(一時ストレージ) とPersistent Storage
の違い、Persistent Storage
のメリットとユースケース、navigator.storage.persist()
APIの使い方と条件、各ブラウザの対応状況と成功率に影響する要因、さらにService Worker
やCache API
と組み合わせた活用例について、技術的に詳しく解説します。
Temporary Storage(一時ストレージ)とPersistent Storage(永続ストレージ)の違い
IndexedDB
のデフォルトの保存領域はTemporary
(一時的) Storage
と呼ばれ、これは ベストエフォート型のストレージ です。一時ストレージでは、ブラウザは可能な限りデータを保持しますが、以下のような条件でデータが削除(エビクト)される可能性があります:
- サイト(オリジン)が割り当てられた容量上限(クォータ)を超えた場合
- デバイス自体のディスク残量が少なくなり、ストレージプレッシャーが発生した場合
- ユーザーがブラウザの設定からそのサイトのデータを手動で削除した場合
一時ストレージで保存されたデータは、ブラウザがストレージ不足時に自動削除する対象となります。この削除処理は通常ユーザーに通知されず、バックグラウンドで行われます。IndexedDB
やCache API
でデータを保存する際、ユーザーに特別な許可を求めることなく透過的にこの一時領域へデータが格納されますが、同様に必要に応じてブラウザはユーザーに断りなくそれらを消去し得ます。これに対し、Persistent
(永続的) Storage
は開発者が明示的にオプトインすることで利用できる特別な保存領域です。永続ストレージに格納されたデータは、ブラウザ側の都合(容量不足など)では削除されず、ユーザーがブラウザの設定から手動で消さない限り保持されます。言い換えれば、永続ストレージ上のデータはユーザーによる明示的な削除操作が行われた場合のみ消去され、ブラウザの自動クリーンアップの対象から外れるということです。なお、IndexedDB
やCache API
のデータはデフォルトでは全て一時ストレージ扱いですが、一度サイトに対してPersistent Storage
の許可が与えられると、そのオリジン(サイト)のすべての保存データが永続的に扱われます。これにはIndexedDB
のデータだけでなく、後述するようにCache API
によるキャッシュ、Cookie、Web Storage(localStorage
など)、サービスワーカーの登録情報など、そのサイトに属するあらゆるストレージが含まれます。したがって、IndexedDB
を永続化するという操作はサイト全体のストレージバケットを永続モードに切り替えることを意味します。ストレージ容量(クォータ)の違いにも触れておきましょう。ブラウザはサイト毎に格納できるデータ量に上限を設けていますが、永続ストレージではその上限が引き上げられる場合があります。例えば Firefox では、一時ストレージの場合は各サイトが使用できる容量は「ディスク全体の10%または10GBのいずれか小さい方」に制限されていますが、永続ストレージが許可されたサイトではディスク容量の50%まで(最大8TiB)使用可能で、かつeTLD+1単位のグループ制限も適用されません。具体例として、500GBのディスクを持つデバイスでは、一時ストレージでは1サイト当たり最大10GBまでしか保存できませんが、永続ストレージであれば最大250GB(500GBの50%)まで保存可能になります。一方 ChromeなどChromium系ブラウザ では、一時・永続に関わらず1サイト当たりディスクの最大60%程度まで使用できるよう設計されており、永続化による容量上限の変化は特にありません(元々十分大きな上限が与えられています)。Safari については後述しますが、最近のバージョンでは通常時は約20%のディスク容量が上限で、ユーザーがウェブアプリをホーム画面やDockに追加した場合は約60%に上限が引き上げられる仕様です。以上まとめると、Temporary Storage
(ベストエフォート型)はデフォルトで使われ利便性は高いもののストレージ不足時にデータ削除のリスクがあり、Persistent Storage
(永続型)は開発者が要求した場合にのみ有効化され、その代わりブラウザによる自動削除が行われないという大きな違いがあります。
Persistent Storageのメリットとユースケース
Persistent Storage
を利用する最大のメリットは、オフラインデータの信頼性を向上できる点です。特に次のようなユースケースでは永続化の恩恵が大きいでしょう。
- オフラインファーストのWebアプリ: ネットワークに接続できない状況でも動作することを重視したアプリでは、データを
IndexedDB
やCache API
に大量に保存します。例として、オフライン対応のノートアプリやToDoアプリ、あるいは旅行中に使う地図アプリなどが挙げられます。こうしたアプリではユーザーがローカルに保存したデータが消えてしまうと大きな問題となります。Persistent Storage
を使えば、デバイスの空き容量が逼迫した場合でもそれらのデータがブラウザによって勝手に削除されることはなく、ユーザーの端末上に安全に保存され続けます。例えば「端末上にのみ保存されクラウドにバックアップされていない暗号鍵」など、失われると大きな影響があるクリティカルなデータはPersistent Storage
で保護することが推奨されています。 - PWA(プログレッシブWebアプリ): PWAとしてインストールされたウェブアプリは、ユーザーにとってネイティブアプリに近い体験を提供します。ユーザーはインストールしたアプリのデータが安易に消えないことを期待するでしょう。
Persistent Storage
を利用すれば、インストール済みPWAが保持するデータ(例えば記事のオフラインキャッシュやユーザー設定情報など)がストレージ不足時でも維持され、アプリは常にオフラインで起動・利用可能な状態を保てます。多くのブラウザ(ChromeやSafariなど)では、ユーザーがそのサイトをインストール済み(またはブックマーク済み)であること自体を「重要なサイト」の指標として扱い、Persistent Storage
のリクエストを自動的に許可する挙動があります(詳細は後述)。そのためPWAではユーザー体験向上のため永続化が特に有用です。 - 大容量データのキャッシング: 動画・音声コンテンツや地図データ、画像資源など、サイズの大きなデータをオフライン用にブラウザへキャッシュするケースでも
Persistent Storage
が役立ちます。通常の一時ストレージではストレージプレッシャー時にこうした大量データは真っ先に削除対象となる可能性があります。しかし永続ストレージを確保しておけば、一度キャッシュした大容量コンテンツがユーザーの明示的操作無しに消えることはなくなります。例えばオフライン再生に対応したメディアプレーヤーやオフライン地図サービスでは、Persistent Storage
により安定してコンテンツを提供できます。
以上のように、Persistent Storage
はオフラインアプリやPWAなどでのクリティカルなデータ保護に威力を発揮します。もっとも前述の通り、実際にはブラウザが自動的にデータを消去する事態は頻繁には起こりません。ユーザーが定期的にアプリを使っていれば、通常は一時ストレージのままでもデータが消える可能性は低いのが現状です。そのため、永続化のリクエストは「本当に重要なデータを守るため」に絞って行うのが望ましく、不必要に多用すべきではないでしょう。例えば定期的にサーバと同期されるデータであれば永続化せずともリスクは小さいですし、一方でユーザーの入力した貴重なデータがローカルにしか無い場合や、長期間アクセスがなくても保持しておきたいデータに対して重点的に使うとよいでしょう。
Persistent Storageを利用する方法とnavigator.storage.persist()
の使い方
ブラウザでPersistent Storage
を利用するには、Storage API(StorageManager
インターフェイス)を通じてストレージ永続化の許可をリクエストする必要があります。具体的には navigator.storage.persist()
というメソッドを呼び出すことで、そのサイトのストレージを永続的に使用したい旨をブラウザに要求できます。このメソッドは Promise
を返し、永続化が許可されストレージバケットのモードがpersistent
に変更された場合はtrue
、拒否された場合はfalse
で解決されます。なお、このAPIはセキュアコンテキスト(HTTPS)でのみ使用可能です。navigator.storage.persist()
を呼び出す前に、現在そのサイトが永続ストレージモードかどうかを確認することもできます。navigator.storage.persisted()
はPromise
を返し、既に永続化が許可されていればtrue
になります。一般的な実装方針として、まずpersisted()
で状態を確認し、必要に応じてpersist()
を呼ぶようにすると良いでしょう。また、一度永続化が許可された後にそれを解除するためのAPIは現状用意されていません(不要になったからといってプログラム的にpersistent
を取り消すことはできず、必要ならユーザーが手動でサイトデータを消去する必要があります)。永続化要求の実装例として、以下にPseudoコードを示します。IndexedDB
を利用する前にストレージ永続化をリクエストし、その結果をログ出力しています(実際にはユーザー操作に応じて呼び出すことが推奨されますが、詳細は後述):
// 永続ストレージのリクエスト関数(例)
async function requestPersistentStorage() {
if (navigator.storage && navigator.storage.persist) {
// すでに永続化されているか確認
const alreadyPersisted = await navigator.storage.persisted();
console.log("Already persisted:", alreadyPersisted);
if (!alreadyPersisted) {
// 永続化をリクエスト
const granted = await navigator.storage.persist();
console.log("Persistent storage granted:", granted);
if (granted) {
console.log("このサイトのストレージは永続化されました。");
} else {
console.log("永続化の許可が得られませんでした。");
}
}
} else {
console.error("Persistent Storage APIがサポートされていません。");
}
}
上記のように、persist()
メソッドを呼ぶことで永続化の許可を求めることができます。しかし、このリクエストが必ず成功するとは限らない点に注意が必要です。各ブラウザはブラウザごとのポリシーに従ってこの要求を許可または拒否します。許可が得られればPromiseはtrue
で解決し、今後はブラウザによる当該オリジンのデータ削除が行われなくなります。一方、拒否された場合はfalse
となり、この場合データは引き続き一時ストレージ扱いとなります。
永続化リクエストのタイミングと前提条件
navigator.storage.persist()
を使うにあたり、いつ・どのようにリクエストするかも重要です。特にFirefoxでは後述するようにユーザーに許可を求めるプロンプトが表示されるため、ユーザーが納得できるタイミングでリクエストすることが成功の鍵となります。推奨されるタイミングは、ユーザーが重要なデータを保存した直後やオフラインモードを有効にした瞬間など、「ユーザー自身が“この操作でデータが保存された”と理解しているタイミング」です。決してページ読み込み時や何も操作していないタイミングで自動的に永続化を求めないことが推奨されています。例えばユーザーが特に保存操作をしていない状況でいきなり「ストレージを永続化しますか?」と尋ねられても困惑し、許可を拒否する可能性が高いでしょう。そのため、永続化が必要な場合でもユーザーの操作に紐づけて(例えば「オフラインで使用するためにデータを保存します」ボタンの処理内で)リクエストするのが望ましいです。また、一度ユーザーに拒否されたのにすぐ再度プロンプトを出す、といった行為も避けるべきです。時間をおいてユーザーのアプリ利用度合いが上がってから再試行するなど配慮しましょう。さらに、ChromeやSafariのようにユーザープロンプトが出ないブラウザでも、ユーザーの明示的操作を介してリクエストを呼ぶことが望ましいです。そうすることで、ブラウザ側のヒューリスティクス(後述)でも「ユーザーがこのサイトを積極的に利用している」と判断され、許可が下りやすくなると考えられるためです。
ブラウザごとの対応状況と永続化リクエストの挙動
Persistent Storage
は比較的新しいWebプラットフォーム機能ですが、主要なモダンブラウザでは広くサポートされています。ただし、その許可(Permission)の扱われ方はブラウザによって異なります。ここではChrome系、Firefox、Safariの挙動と、許可の成功率に影響を与える要因について解説します。
Chrome/Chromium系ブラウザの場合
ChromeおよびChromiumベースのブラウザ(Edgeなど)では、Persistent Storage
の許可要求に対してユーザーに確認のポップアップを表示しません。代わりに、ブラウザ内部のヒューリスティクス(経験則的な指標)に基づいて自動的に許可または拒否を判断します。サイトが「重要」であると見なされた場合は要求は自動的に承認され、それ以外の場合はユーザーに知らせることなく静かに拒否されます。Chromeがサイトの重要度を判断する主な要因として、次のような指標が知られています:
- サイトエンゲージメント: ユーザーがそのサイトと頻繁かつ継続的にインタラクションしているか(利用頻度や滞在時間などの総合的な評価)
- インストールまたはブックマーク: ユーザーがそのサイトをPWAとしてデバイスにインストール済み、またはブラウザにブックマーク登録しているか
- 通知許可: プッシュ通知など、そのサイトに対して他の権限(通知の表示許可)が与えられているか
これらの条件のいずれかを満たすサイトはユーザーにとって重要度が高いと推測されるため、ブラウザはPersistent Storage
の権限を自動付与します。逆に、これらに該当しないサイトからのリクエストは裏で自動的に拒否され、navigator.storage.persist()
の結果はfalse
となります(ユーザーには何も表示されません)。一度拒否された場合でも、例えばユーザーがその後サイトをインストールするなど状況が変われば、改めてリクエストすれば許可される可能性があります。Chromeではこのようにユーザーの操作履歴やサイトに対する好意度が許可の成否を大きく左右します。
Firefoxの場合
FirefoxではPersistent Storage
のリクエスト時にユーザーへの確認ダイアログが表示されます。開発者がnavigator.storage.persist()
を呼び出すと、Firefoxは画面上部にサイト固有の権限リクエストポップアップを出し、ユーザーに許可を求めます。ユーザーが「許可」を選択すればPersistent Storage
が有効になり、拒否(または「後で」を選択)すれば有効になりません。そのため、Firefoxにおいてはユーザーの判断が永続化の可否を直接決定します。
(注: 元記事にあったFirefoxのポップアップ画像の説明に該当する部分です。) FirefoxではPersistent Storage
をリクエストすると、許可確認のポップアップが表示されます。ユーザーが「Allow(許可)」をクリックすると、そのサイトのストレージは永続的に保持されるようになります。一方「Not Now」を選ぶかポップアップを閉じた場合、要求は拒否され(一時ストレージのままとなり)、再度同じサイトでリクエストしても再びダイアログが表示されます。したがって開発者は、Firefoxユーザーにとって不自然でないタイミング(前述のようにユーザーがデータ保存操作を行った直後など)でこのダイアログを出すことが重要です。
Safari(およびその他のブラウザ)の場合
Safari(WebKitベースのブラウザ)もPersistent Storage API
をサポートしていますが、その挙動はChrome系に近いと考えられます。Safariや一部他のChromium系ブラウザ(Opera等)では、ユーザーへのプロンプトは表示されず、ユーザーのサイト利用状況に基づいて自動的に許可・拒否を判断する仕様です。つまりChrome同様、Safariでもpersist()
呼び出し時に何も表示されず、内部ロジックにより許可されるかどうかが決まります。ではSafariにおいて「重要なサイト」と見なされる条件は何かというと明確な公表はありませんが、一般にユーザーが積極的に利用しているサイトかどうか(頻繁に訪問している、ホーム画面に追加している等)が基準になると考えられます。実際、Safariでは iOS17 / macOS Sonoma (Safari 17) 以降、ストレージクォータ(容量上限)周りの仕様が大きく変更されました。従来は1GBの初期上限を超えると都度ユーザーに「このサイトはより多くのストレージを使用しようとしています...」という許可ダイアログを表示していましたが、Safari 17からは各サイトに対しディスク容量に基づく余裕ある上限が自動割当されるようになり、通常のブラウジング中にユーザープロンプトが出ることはなくなりました。この新仕様では各サイトはデフォルトでディスクの約20%までデータを保存でき、さらにユーザーがそのサイトをホーム画面やDockに追加してウェブアプリ化している場合は上限が約60%まで引き上げられます。また明示的にPersistent Storage
が許可されたオリジンは、ストレージ不足時の削除対象から除外されます。Storage API(persist()
やestimate()
等)もSafari 17で正式にフルサポートされ、永続モードの状態はセッションをまたいで保持されるよう改善されました。まとめると、2025年現在ではChrome/Edge、Firefox、Safari、Operaといった主要ブラウザはいずれもPersistent Storage
に対応しています。その最低対応バージョンはChrome 55+、Firefox 57+、Safari 15.2+ (iOS15.2+)、Edge 79+などと報告されています。ただ許可フローはブラウザごとに異なり、Chrome系とSafariはユーザー操作履歴に基づく自動判定(ユーザーへの明示的な確認なし)、Firefoxはユーザーへの許可ダイアログによる確認、と覚えておくとよいでしょう。また、ユーザーがサイトをインストールしていたり通知を許可しているといった条件は、ChromeやSafariでは永続化成功の大きな後押しとなります。一方Firefoxでは最終的にユーザーのクリック次第なので、開発者側でタイミングや説明に工夫を凝らすことが求められます。
Service WorkerやCache APIと組み合わせた活用例
Persistent Storage
はIndexedDB
単体だけでなくサイト全体のストレージデータを保護することを述べました。そのため、Service Worker
やCache API
と組み合わせることでオフラインWebアプリの信頼性を飛躍的に高めることができます。ここでは、PWAを例に具体的な活用シナリオを考えてみます。シナリオ: オフライン対応のPWAを作成しており、Service Worker
で静的ファイルをキャッシュ(Cache API
使用)し、IndexedDB
にユーザーデータを保存しているとします。ユーザーが「オフラインモードを有効にする」操作を行った際に、必要なリソースをキャッシュしデータを保存した上でPersistent Storage
を要求する流れを実装します。これにより、キャッシュしたファイルやIndexedDB
内のデータは永続ストレージに守られ、仮にデバイスの空き容量が厳しくなってもブラウザから削除されないようにします。以下はその擬似コード例です。
// オフライン用データ保存と永続化リクエストの例
async function enableOfflineMode() {
// 1. 静的リソースをCache APIにキャッシュする
const cache = await caches.open('myapp-static-v1');
await cache.addAll([
'/offline.html',
'/app.css',
'/app.js',
// ...必要な他のリソース
]);
console.log('静的リソースをキャッシュに保存しました。');
// 2. IndexedDBにユーザーデータを保存する(例として設定情報を保存)
const db = await openMyIndexedDB(); // IndexedDBデータベースを開く(実装は省略)
const tx = db.transaction('settings', 'readwrite');
tx.objectStore('settings').put({ offlineEnabled: true }, 'offlineMode'); // 設定項目を書き込み
await tx.complete;
console.log('ユーザー設定データをIndexedDBに保存しました。');
// 3. ストレージを永続化するようリクエストする
if (navigator.storage && navigator.storage.persist) {
const granted = await navigator.storage.persist();
if (granted) {
console.log('ストレージ永続化が許可されました。オフラインデータが保護されます。');
} else {
console.log('ストレージ永続化の許可が得られませんでした。');
}
}
}
上記のコードでは、(1) Cache API
を使って必要なファイルをキャッシュし、(2) IndexedDB
にオフライン用のフラグを保存した後、(3) navigator.storage.persist()
で永続化を要求しています。実際にはService Worker
のインストール時にキャッシュ保存し、ユーザー操作時に永続化だけ行う方が望ましい場合もありますが、ここでは分かりやすさのため一連の処理をまとめています。重要なのは、persist()
の呼び出しはメインスレッド(window)側で行う必要がある点です。Service Worker
内部ではNavigator.storage
にアクセスできませんので、必要に応じてpostMessage
等で永続化リクエストをメインスクリプトに委譲します。Persistent Storage
が許可されれば、このサイトのCache API
データ(静的リソース群)やIndexedDB
データ、Service Worker
の登録情報などすべてがブラウザのエビクション対象外となります。つまり、ユーザーが明示的にデータ消去しない限り、オフラインに必要なファイルやデータは常にデバイス上に残り続けます。これにより、ユーザーが長期間アプリを開かない場合でもキャッシュが消えてしまったり、次に起動したときにオフラインで使えない、といった事態を防止できます。特にモバイル環境ではストレージ不足によるシステムの一括キャッシュ削除などが起こり得るため、Persistent Storage
による保護はオフライン対応PWAにとって大きな安心材料となるでしょう。
まとめ
IndexedDB
をはじめとするブラウザのストレージをPersistent Storage
(永続ストレージ)として扱うことで、ウェブアプリのオフラインデータを堅牢に保護できることを見てきました。デフォルトの一時ストレージでは利便性が高い反面、ストレージプレッシャー時にデータ消失のリスクがありますが、永続ストレージを適切に利用すればブラウザが自動でデータを消すことはなくなり、ユーザーにとって安心感のあるオフライン体験を提供できます。特にサーバに存在しない重要データをローカルに保存するPWAでは、この機能がデータ損失防止の強力な手段となります。もっとも、Chromeチームの調査が示すように通常はデータが自動的に消されるケース自体稀ではありますが、万一消えると大きな問題となる情報についてはPersistent Storage
で保護する価値があります。導入にあたっては、本記事で述べた各ブラウザの挙動の違いやユーザー許可フローを踏まえ、ユーザーが許可しやすいタイミングで要求する、必要以上に乱用しないといった点に留意してください。適切にPersistent Storage
を活用することで、ウェブアプリであってもネイティブアプリに劣らない信頼性の高いデータ保持を実現できるでしょう。
参考資料: Storage API / Persistent Storageに関する公式ドキュメントやブログ記事(MDN、Chrome Developers/Web.devなど)も合わせて参照してください。今回触れたStorageManager
API以外にもnavigator.storage.estimate()
による容量確認など有用な機能がありますので、興味があれば調べてみてください。Persistent Storage
を適切に活用して、オフラインでもユーザーデータをしっかり守るウェブアプリケーションを構築しましょう。