localStorageにアクセスできない原因と解決策まとめ

localstorage-not-working JavaScript
記事内に広告が含まれています。

「保存したはずのデータが消えている」「localStorage is not definedというエラーで処理が止まってしまった」——JavaScriptやReact、Next.jsで開発をしていると、こうしたlocalStorageのトラブルに一度は突き当たります。原因はコードの書き方だけでなく、実行環境やブラウザの仕様、本番特有のオリジンの違いなど多岐にわたるため、闇雲に修正しても解決しないことも少なくありません。

本記事では、SecurityErrorやQuotaExceededErrorといった具体的なエラーから、Next.jsのSSRでwindowが使えない問題、本番環境だけでデータが消える現象まで、原因と対処法を体系的に整理しました。エラーメッセージから逆引きできる構成になっているので、今まさにエラーに悩んでいる方はもちろん、今後同じ問題を再発させたくない方にも役立つ内容です。

この記事を読んでわかること

  • localStorage is not defined・SecurityError・QuotaExceededErrorが発生する具体的な原因
  • try...catchやChrome DevToolsを使ったエラーの確認・切り分け方法
  • Node.js・React・Next.js・Vue.js・AstroでlocalStorageを安全に使うための実装パターン
  • 本番環境やSafariなど特定のブラウザだけでデータが消える理由
  • IndexedDBやサーバーサイド保存など、localStorageが使えない場合の代替手段と選び方
  1. localStorageにアクセスできない主な原因
    1. localStorage is not definedが発生する実行環境
    2. SecurityErrorが発生するオリジン・プロトコルの条件
    3. QuotaExceededErrorが発生する容量制限
    4. file://・iframe・クロスオリジンでブロックされるケース
  2. JavaScriptでlocalStorageが動かないときの確認方法
    1. localStorage.getItem()・setItem()の基本的な使い方
    2. Chrome DevToolsで保存データとオリジンを確認する方法
    3. try...catchでSecurityErrorとQuotaExceededErrorを判定する方法
    4. localStorage.clear()・removeItem()でデータを削除・初期化する方法
  3. Node.js・React・Next.jsなどでlocalStorageが使えない理由
    1. Next.jsのSSR・React Server Componentsでwindowが使えない原因
    2. typeof window !== 'undefined'とuseEffect()でクライアント側だけ実行する方法
    3. Node.js・React・Vue.js・AstroでlocalStorageを安全に扱う方法
  4. 本番環境やブラウザでデータが消える原因
    1. HTTP・HTTPS・ドメイン変更で保存先オリジンが分かれる仕組み
    2. Chrome・Firefox・Edge・Safariの保存制限と挙動の違い
    3. SafariのITPとプライベートブラウジングによる制限
    4. PWA・WebView・アプリ内ブラウザで保存データが消えるケース
    5. キャッシュクリア・Cookie削除・SameSite属性の影響範囲
  5. localStorageが使えないときの代替手段と使い分け
    1. Cookie・sessionStorage・localStorageの違いと選び方
    2. IndexedDBを使って大容量データを保存する方法
    3. ログイン情報やユーザー設定をサーバーサイドで永続化する方法
  6. よくある質問(FAQ)
  7. まとめ

localStorageにアクセスできない主な原因

localStorageにアクセスできない原因は、大きく分けて「実行環境がブラウザではない」「オリジン(URLの起源となるプロトコル・ドメイン・ポート番号の組み合わせ)がセキュリティ条件を満たしていない」「保存容量の上限を超えている」の3パターンです。エラーメッセージの種類によって原因が明確に切り分けられるため、まずはどのエラーが出ているかを確認しましょう。

localStorageにアクセスできない主な原因

localStorage is not definedが発生する実行環境

このエラーは、windowオブジェクトが存在しない環境でlocalStorageを呼び出したときに発生します。代表的なのはNode.jsのサーバーサイド実行環境や、Next.jsのサーバーサイドレンダリング(SSR)処理中です。

localStorageはブラウザが提供するWeb APIであり、Node.js単体には存在しません。

// Node.js環境で実行するとエラーになる
localStorage.setItem('key', 'value');
// ReferenceError: localStorage is not defined

つまずきやすいポイント:Next.jsやNuxt.jsのようなSSR対応フレームワークでは、コンポーネントの初回描画がサーバー側で行われるため、うっかりトップレベルでlocalStorageを呼び出すとビルドやレンダリングが失敗します。

SecurityErrorが発生するオリジン・プロトコルの条件

SecurityErrorは、ブラウザのセキュリティポリシーによってlocalStorageへのアクセス自体が拒否されたときに発生します。主な原因は、file://プロトコルでの直接実行や、Cookieやストレージをブロックする設定が有効になっている場合です。

try {
  localStorage.setItem('test', '1');
} catch (e) {
  if (e.name === 'SecurityError') {
    console.error('ストレージへのアクセスが拒否されました');
  }
}

よくある間違い:ローカルのindex.htmlをfile://で直接開いて動作確認し、「本番と同じはずなのに動かない」と誤解してしまうケースです。必ずローカルサーバー(http://localhostなど)経由で確認しましょう。

QuotaExceededErrorが発生する容量制限

QuotaExceededErrorは、localStorageの保存容量の上限(多くのブラウザでオリジンごとに5MB前後)を超えたときに発生します。大きな画像データやJSON化した大量のオブジェクトを保存しようとすると発生しやすいエラーです。

try {
  localStorage.setItem('bigData', hugeString);
} catch (e) {
  if (e.name === 'QuotaExceededError') {
    console.error('保存容量の上限に達しました');
  }
}

つまずきやすいポイント:容量上限はブラウザやモード(通常/プライベート)によって異なるため、「昨日は保存できたのに今日はエラーになる」という現象が起こり得ます。上限ぎりぎりの設計は避けましょう。

file://・iframe・クロスオリジンでブロックされるケース

localStorageはオリジン単位で分離されており、file://プロトコル、サードパーティのiframe内、異なるドメイン間では正しくアクセスできない、または完全に分離されたストレージ領域として扱われます。

実行条件localStorageの挙動
http://またはhttps://の通常ページ正常に読み書き可能
file://で直接開いたHTMLブラウザによりアクセス不可またはエラー
同一オリジンのiframe親ページと同じデータを共有
クロスオリジンのiframe別オリジンとして完全に分離されたストレージ

よくある間違い:親ページとiframe内で「同じデータが見える」と思い込んでしまうことです。オリジンが異なれば、localStorageのデータは共有されません。

◆◇◆ 【衝撃価格】VPS512MBプラン!1時間1.3円【ConoHa】 ◆◇◆

JavaScriptでlocalStorageが動かないときの確認方法

localStorageの不具合を切り分けるには、まず基本メソッドが正しく動いているかをコンソールで確認し、次にDevToolsで保存先オリジンとデータの中身を目視確認するのが最短ルートです。エラーが出る場合はtry...catchで例外の種類を判定すれば、原因をピンポイントで特定できます。

JavaScriptでlocalStorageが動かないときの確認方法

localStorage.getItem()・setItem()の基本的な使い方

setItem(key, value)でデータを保存し、getItem(key)で取得します。値は必ず文字列として保存されるため、オブジェクトや配列を扱う場合はJSON.stringify()とJSON.parse()を組み合わせます。

// 保存
localStorage.setItem('username', 'taro');

// 取得
const name = localStorage.getItem('username');
console.log(name); // "taro"

// オブジェクトを保存する場合
const user = { id: 1, name: 'taro' };
localStorage.setItem('user', JSON.stringify(user));

// 取得して復元(※不正なデータによるエラーを防ぐためtry...catchを使用)
try {
  const storedUser = localStorage.getItem('user');
  if (storedUser) {
    const restoredUser = JSON.parse(storedUser);
    console.log(restoredUser);
  }
} catch (e) {
  console.error('データの復元に失敗しました。データが破損している可能性があります:', e);
}

つまずきやすいポイント:存在しないキーをgetItem()するとundefinedではなくnullが返ります[cite: 1]。また、取得した値をJSON.parse()する際、ストレージ内のデータが破損しているとSyntaxErrorが発生するため、オブジェクトを復元する際は必ずtry…catchでエラーハンドリングを行いましょう。

Chrome DevToolsで保存データとオリジンを確認する方法

DevToolsの「Application」タブ(Firefoxでは「ストレージ」タブ)を開き、左側の「Local Storage」からオリジンを選択すると、保存されているキーと値の一覧を確認できます。オリジンが想定と違う場合、データが見つからない原因の多くはここで判明します。

  1. DevToolsを開く(F12または右クリック→検証)
  2. 「Application」タブ→左メニューの「Local Storage」を展開
  3. 対象のオリジン(https://example.comなど)をクリック
  4. 一覧に表示されるキーと値を確認する

よくある間違い:http://とhttps://、またはwww.の有無だけでも別オリジンとして扱われるため、DevTools上で「データがない」と表示された際は、まずアドレスバーのURLとオリジンが一致しているかを確認しましょう。

try...catchでSecurityErrorとQuotaExceededErrorを判定する方法

localStorageの操作はtry...catch構文で囲み、error.nameプロパティを見ることでSecurityErrorとQuotaExceededErrorを区別できます。これにより、エラーの種類に応じたユーザー向けメッセージやフォールバック処理を実装できます。

function safeSetItem(key, value) {
  try {
    localStorage.setItem(key, value);
    return true;
  } catch (e) {
    if (e.name === 'QuotaExceededError') {
      console.warn('容量オーバーです。不要なデータを削除してください。');
    } else if (e.name === 'SecurityError') {
      console.warn('このブラウザ設定ではlocalStorageを利用できません。');
    } else {
      console.error('予期しないエラー:', e);
    }
    return false;
  }
}

つまずきやすいポイント:e.nameではなくe.messageの文字列で判定しようとすると、ブラウザによってメッセージ文言が異なるため誤判定の原因になります。必ずnameプロパティで判定しましょう。

localStorage.clear()・removeItem()でデータを削除・初期化する方法

特定のキーだけを削除したい場合はremoveItem(key)、保存データを全て消したい場合はclear()を使います。バグ調査時にストレージを初期状態に戻したいときにもよく使われます。

// 特定のキーを削除
localStorage.removeItem('username');

// 全データを削除
localStorage.clear();

よくある間違い:clear()は同一オリジン内のすべてのキーを削除します。他の機能が使っているデータまで巻き込んで消してしまうことがあるため、本番コードでの安易な使用は避けましょう。

Window: localStorage プロパティ - Web API | MDN
localStorage は window インターフェイスの読み取り専用プロパティで、この Document の origin における Storage オブジェクトにアクセスできます。格納されたデータは、ブラウザーのセッションを跨いで保存されます。

コストパフォーマンスに優れた高性能なレンタルサーバー

【Hostinger】

Node.js・React・Next.jsなどでlocalStorageが使えない理由

Node.js・React・Next.jsでlocalStorageが使えない最大の理由は、これらの環境の一部がブラウザ外(サーバーサイド)で実行されるためです。windowオブジェクトが存在しないタイミングでコードが評価されると、localStorage is not definedエラーが発生します。

Node.js・React・Next.jsなどでlocalStorageが使えない理由

Next.jsのSSR・React Server Componentsでwindowが使えない原因

Next.jsのSSR(サーバーサイドレンダリング)やReact Server Componentsは、Node.jsサーバー上でHTMLを生成する仕組みです。このプロセスにはブラウザが提供するwindowやlocalStorageが存在しないため、コンポーネントのトップレベルやレンダリング中にlocalStorageへアクセスするコードを書くとエラーになります。

// NGな例:コンポーネントのトップレベルで実行される
function Profile() {
  const name = localStorage.getItem('username'); // SSR時にエラー
  return <p>{name}</p>;
}

つまずきやすいポイント:ローカル開発では気づかず、Vercelなどの本番環境でビルド・SSRが走った瞬間にエラーが表面化するケースが多く見られます。

typeof window !== 'undefined'とuseEffect()でクライアント側だけ実行する方法

typeof window !== 'undefined'という条件式で実行環境を判定し、ブラウザでのみコードを実行するようにガードします。Reactでは、コンポーネントの描画完了後(クライアント側)に一度だけ実行されるuseEffect()内にlocalStorage処理を書くのが基本パターンです。

import { useEffect, useState } from 'react';

function Profile() {
  const [name, setName] = useState('');

  useEffect(() => {
    if (typeof window !== 'undefined') {
      const stored = localStorage.getItem('username');
      if (stored) setName(stored);
    }
  }, []);

  return <p>{name}</p>;
}

useEffect()はブラウザ上でのレンダリング後にのみ実行されるため、この中に書いたコードはSSRの影響を受けません。

よくある間違い:typeof window !== 'undefined'のチェックだけをuseEffect()の外(コンポーネント本体の直下)に書いてしまい、結局SSR時にも評価されてエラーになるケースです。ガードとuseEffectはセットで使いましょう。

Node.js・React・Vue.js・AstroでlocalStorageを安全に扱う方法

フレームワークごとに「クライアント側限定で実行する」ための仕組みが用意されています。それぞれの作法に沿ってlocalStorageアクセスを実装することで、SSRエラーを防げます。

環境localStorageが使えないタイミング安全に使う方法
Node.js(単体)常に利用不可ブラウザ向けコードと分離する
ReactSSRやコンポーネント初回評価時useEffect()内で実行
Next.jsSSR・RSC(Reactサーバーコンポーネント)実行時'use client'ディレクティブを指定した上で、useEffect()内で実行(※'use client'単体では初回SSR時にエラーになります)
Vue.jsSSR(Nuxt.jsなど)のサーバー処理時onMounted()ライフサイクル内で実行
Astroサーバーサイドレンダリング時<script>タグやクライアントディレクティブ内で実行

つまずきやすいポイント:フレームワークが違えば「クライアント側でのみ実行する」ためのAPI名も異なります(React:useEffect、Vue:onMounted)。移植時に名前だけ変えて構造を変えないよう注意しましょう。

新世代レンタルサーバー『シンレンタルサーバー』

本番環境やブラウザでデータが消える原因

本番環境でlocalStorageのデータが消える主な原因は、「オリジンの変更」「ブラウザ独自の保存制限」「プライベートブラウジングモード」「アプリ内ブラウザやPWAの特殊な挙動」の4つです。ローカル開発では再現しにくいため、本番特有の落とし穴として押さえておく必要があります。

本番環境やブラウザでデータが消える原因

HTTP・HTTPS・ドメイン変更で保存先オリジンが分かれる仕組み

localStorageはプロトコル・ドメイン・ポート番号の組み合わせ(オリジン)ごとに独立して保存されます。開発時はhttp://、本番はhttps://というように、プロトコルが変わるだけでも別オリジン扱いとなり、以前保存したデータにはアクセスできなくなります。

変更前変更後同一オリジンか
http://example.comhttps://example.com別オリジン(プロトコルが異なる)
https://example.comhttps://www.example.com別オリジン(サブドメインが異なる)
https://example.com:3000https://example.com:8080別オリジン(ポートが異なる)
https://example.com/page1https://example.com/page2同一オリジン(パスは無関係)

つまずきやすいポイント:独自ドメインへの移行やHTTPS化のタイミングで「ユーザーのログイン情報が消えた」という問い合わせが増えるのは、この仕組みが原因であることがほとんどです。

Chrome・Firefox・Edge・Safariの保存制限と挙動の違い

主要ブラウザは基本的にlocalStorageの仕様(Web Storage API)に準拠していますが、保存容量の目安や無操作時の削除ポリシーには差があります。

ブラウザ保存容量の目安長期間未使用時の挙動
Chromeオリジンごと約5MB前後基本的に自動削除はされにくい
Firefoxオリジンごと約5MB前後基本的に自動削除はされにくい
Edge(Chromium版)Chromeとほぼ同等Chromeとほぼ同等
Safariオリジンごと約5MB前後ITPにより一定期間で削除される場合がある

よくある間違い:容量の数値はブラウザのバージョンや設定によって変動するため、「〇〇MBまで確実に使える」と決め打ちで設計してしまうことです。余裕を持った容量設計を心がけましょう。

SafariのITPとプライベートブラウジングによる制限

SafariのITP(Intelligent Tracking Prevention、トラッキング防止機能)は、一定期間サイトへのアクセスがない場合にlocalStorageを含むスクリプトによる保存データを削除することがあります。また、プライベートブラウジングモードでは、通常モードと保存領域が分離されるか、容量が大幅に制限される場合があります。

// プライベートモードなどでsetItemが失敗する可能性を考慮する
try {
  localStorage.setItem('test', '1');
  localStorage.removeItem('test');
} catch (e) {
  console.warn('この環境ではlocalStorageが制限されています');
}

つまずきやすいポイント:「Safariだけデータが消える」という報告を受けた際、バグだと思い込んで実装を疑う前に、ITPやプライベートモードの仕様である可能性を確認しましょう。

PWA・WebView・アプリ内ブラウザで保存データが消えるケース

PWA(Webアプリをネイティブアプリのように利用できる仕組み)やSNSアプリ内蔵のWebView(アプリ内ブラウザ)は、通常のブラウザとは異なるストレージ管理ポリシーを持つことがあります。特にiOSでは、PWAとSafari本体でストレージが共有されない場合があり、アプリ内ブラウザでは容量制限が厳しく設定されていることもあります。

よくある間違い:「ホーム画面に追加したPWAでログインし直しになる」という現象は、実装のバグではなく、ストレージ領域がSafari本体と分離されていることが原因である場合が多いです。

キャッシュクリア・Cookie削除・SameSite属性の影響範囲

ユーザーがブラウザの「閲覧履歴とCookieを削除」する操作を行うと、多くのブラウザではlocalStorageも同時に削除対象となります。一方で、SameSite属性はCookieに関する仕様であり、localStorage自体の挙動には直接影響しません。

操作・設定localStorageへの影響
Cookie削除(単体)ブラウザにより影響する場合としない場合がある
「閲覧データを削除」(履歴・Cookie・キャッシュ一括)多くの場合、localStorageも削除される
SameSite属性の設定変更localStorageには影響しない(Cookie専用の仕様)

つまずきやすいポイント:SameSiteはCookieのクロスサイト送信を制御する仕組みであり、localStorageの削除や保護には関与しません。両者を混同しないようにしましょう。

localStorageが使えないときの代替手段と使い分け

localStorageが使えない、または適さない場面では、用途に応じて「Cookie」「sessionStorage」「IndexedDB」「サーバーサイド保存」を使い分けるのが基本方針です。保存期間・容量・サーバーとの連携有無を基準に選択します。

Cookie・sessionStorage・localStorageの違いと選び方

Cookieはサーバーへの自動送信が必要な小さなデータ、sessionStorageはタブを閉じるまでの一時的なデータ、localStorageはタブを閉じても消えない永続的なデータの保存に向いています。

項目CookiesessionStoragelocalStorage
保存容量の目安約4KBオリジンごと約5〜10MBオリジンごと約5〜10MB
有効期限設定した期限まで(明示的に指定可能)タブ・ウィンドウを閉じるまで明示的に削除するまで半永久的
サーバーへの自動送信あり(リクエストごとに送信)なしなし
主な用途認証トークン、セッションIDフォームの一時保持、タブ単位の状態管理ユーザー設定、ログイン状態の保持など

よくある間違い:機密性の高い認証トークンをlocalStorageに保存してしまうことです。localStorageはJavaScriptから自由に読み取れるため、XSS(クロスサイトスクリプティング)攻撃のリスクを考慮し、認証情報の扱いには注意が必要です。

IndexedDBを使って大容量データを保存する方法

IndexedDBは、ブラウザに搭載されたNoSQL型のデータベースで、localStorageよりもはるかに大きな容量(数百MB〜数GB規模)を扱え、オブジェクトをそのまま保存できるのが特徴です。画像データや大量の一覧データを扱う場合に適しています。

const request = indexedDB.open('myDatabase', 1);

request.onupgradeneeded = (event) => {
  const db = event.target.result;
  db.createObjectStore('users', { keyPath: 'id' });
};

request.onsuccess = (event) => {
  const db = event.target.result;
  const tx = db.transaction('users', 'readwrite');
  const store = tx.objectStore('users');
  store.put({ id: 1, name: 'taro' });
};

つまずきやすいポイント:IndexedDBは非同期APIであるため、localStorageのような同期処理の感覚でコードを書くと、データ取得前に後続処理が実行されてしまうことがあります。onsuccessなどのコールバックやPromiseラッパーを使った制御が必須です。

ログイン情報やユーザー設定をサーバーサイドで永続化する方法

複数端末での同期が必要なデータや、機密性の高い情報は、ブラウザのストレージではなくサーバー側のデータベースで管理するのが安全です。クライアントは認証済みのAPIリクエストを通じてデータを取得・保存し、ブラウザ側には最小限の識別情報のみを保持します。

// サーバーに設定を保存する例
async function saveUserSettings(settings) {
  await fetch('/api/settings', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(settings),
  });
}

よくある間違い:「サーバー保存に切り替えれば安心」と考え、通信エラー時のフォールバック処理を用意しないことです。オフライン時や通信失敗時の挙動も設計に含めましょう。

よくある質問(FAQ)

モバイルブラウザ(スマートフォン)でもlocalStorageの挙動はPCと同じですか?

基本的な仕様はPCと同じですが、iOSのSafariではITPの影響を受けやすく、一定期間アクセスがないとデータが削除される場合があります。Android版Chromeなどは、PC版とほぼ同様の挙動になります。

localStorageの容量の目安はどれくらいですか?

安全な下限値として「オリジンごと5MB程度」が目安となります。ブラウザやバージョンによってはそれ以上の保存が可能な場合もありますが、正確な数値を保証する仕様はないため、5MBを上限の基準とした余裕を持ったデータ設計が推奨されます

プライベートブラウジング(シークレットモード)では何時間くらいデータが保持されますか?

「何時間」という明確な仕様は定義されておらず、多くのブラウザではプライベートウィンドウ・タブを閉じた時点でデータが破棄されます。時間経過ではなく「ウィンドウを閉じるまで」が基準になる点に注意してください。

localStorageに保存したデータは暗号化されていますか?

いいえ、localStorageのデータは平文でブラウザに保存され、暗号化は行われません。パスワードやトークンなどの機密情報をそのまま保存するのは避け、必要であればサーバー側での管理を検討してください。

localStorageとCookieでSameSite属性のような送信制御はできますか?

できません。SameSite属性はCookie固有の仕様であり、localStorageにはクロスサイトでの送信という概念自体が存在しません。localStorageは常に同一オリジン内でのみ読み書き可能です。

ConoHa AI Canvas|ブラウザだけでできる本格的なAI画像生成

まとめ

localStorageにアクセスできない問題は、エラーメッセージと発生環境を切り分けることでほとんどの原因を特定できます。最後に要点を整理します。

  • localStorage is not definedはブラウザ外(Node.jsやSSR処理中)での実行が原因
  • SecurityErrorはfile://実行やストレージブロック設定などセキュリティ条件が原因
  • QuotaExceededErrorは保存容量の上限超過が原因
  • Next.jsなどSSR環境ではtypeof window !== 'undefined'とuseEffect()(VueならonMounted())でクライアント側限定の処理にする
  • 本番でデータが消える場合は、オリジンの変更・Safariの ITP・プライベートモード・PWA特有のストレージ分離を疑う
  • 大容量データはIndexedDB、機密情報や複数端末同期が必要なデータはサーバーサイド保存への切り替えを検討する

まずはtry...catchとDevToolsで原因を特定し、恒常的に容量や機密性の課題がある場合は、IndexedDBやサーバーサイド保存への移行を早めに検討しましょう。

WEBCOACH|副業・フリーランス特化型のオンラインWebデザインスクール
タイトルとURLをコピーしました