紀錄工作經驗、相關知識,解決技術相關問題。

HTML, Javascript, React, 工作雜談, 新手 Jamp Holes, 網站相關

前端資料儲存 Cookie、localStorage、sessionStorage 使用場景、限制、差異

前端資料儲存 Cookie、localStorage、sessionStorage 使用場景、限制、差異 特色圖片

在前端專案開發中,如何在瀏覽器端妥善做前端資料儲存與狀態管理,經常會面臨 Cookie、localStorage 與 sessionStorage 三種機制的選擇。這三者在容量上限、存活週期、跨分頁作用域以及是否隨 HTTP 請求自動傳送給伺服器上都有本質差異。若想單獨查看 localStorage 的基礎存取範例,可先參考 localStorage 用法;而涉及跨網域限制與 Cookie 安全防禦的部分,則可搭配 瀏覽器同源策略 Same-origin policy 說明 與 HTTPOnly Cookie 怎麼設定?新手工程師常見 XSS 防禦實作 一起對照。

一、Cookie、localStorage 與 sessionStorage 規格總覽

在進入各別語法實作前,先透過下方對照表快速釐清三者在容量、生命週期、作用域與伺服器互動上的核心差異,避免在架構設計時產生非預期的效能瓶頸或資安風險:

比較項目CookielocalStoragesessionStorage
儲存容量約 4KB(單一網域也有數量上限)約 5MB(依瀏覽器實作而異)約 5MB(依瀏覽器實作而異)
伺服器傳輸每次同網域 HTTP 請求皆自動夾帶於 Header僅存於瀏覽器端,不隨請求發送僅存於瀏覽器端,不隨請求發送
生命週期依 Expires / Max-Age 決定;未設則為 Session Cookie永久保留,直到程式碼或使用者手動清除僅限當前分頁,關閉該分頁即自動清空
作用域(共享範圍)依 Domain 與 Path 設定,可跨同源分頁與指定子網域共享嚴格遵循同源政策(Origin),同源的多個分頁與視窗可共享嚴格遵循同源政策 + 單一分頁隔離(不同分頁間不共享)

二、三大前端儲存機制的運作原理與程式碼實作

1. Cookie

Cookie 是瀏覽器用來儲存小型鍵值對(Key-Value)文字資料的機制,常用於記錄使用者的登入工作階段(Session ID)、語系偏好或追蹤識別碼。

它的核心特性在於與伺服器雙向溝通:當瀏覽器向符合 Domain 與 Path 條件的伺服器發起 HTTP 請求時,會自動將對應的 Cookie 夾帶在 Cookie 請求標頭(Request Header)中送出。

在前端 JavaScript 中,可以透過 document.cookie 讀寫非機密的 Cookie,並設定 max-age、expires、path、Secure 與 SameSite 等屬性。但需特別注意:關鍵的防 XSS 屬性 HttpOnly 僅能由後端伺服器透過 HTTP 回應標頭(Set-Cookie)設定,前端 document.cookie 無法寫入也無法讀取帶有 HttpOnly 的 Cookie(關於未設定過期時間時的預設行為,可延伸閱讀 Cookie Expires 顯示為 Session 是什麼意思?原因與設定教學)。

// 1. 前端設定一般 Cookie,包含有效期限(7 天 = 604800 秒)與安全傳輸屬性
// 注意:HttpOnly 屬性必須由後端 Set-Cookie 回應標頭設定,不可由 document.cookie 寫入
document.cookie = "theme=dark; max-age=604800; path=/; Secure; SameSite=Strict";

// 2. 讀取目前前端可存取的 Cookie 字串(不含 HttpOnly Cookie)
const allCookies = document.cookie;
console.log(allCookies);

// 3. 刪除 Cookie(將 max-age 設為 0 或將 expires 設為過去時間,且 path 需一致)
document.cookie = "theme=; max-age=0; path=/";

2. localStorage

localStorage 屬於瀏覽器提供的 Web Storage API,允許開發者將資料以「字串型別的鍵值對」長期保存在使用者的瀏覽器中。與 Cookie 最大的不同在於,localStorage 的資料完全不會隨著 HTTP 請求發送給伺服器,且即使關閉瀏覽器分頁、關閉整個瀏覽器視窗或重新開機,資料依然會保留,直到透過程式呼叫清除或使用者手動清除瀏覽器網站資料為止。

在 JavaScript 中,主要使用 localStorage.setItem()、localStorage.getItem()、localStorage.removeItem() 以及 localStorage.clear() 進行管理。由於 Web Storage 僅能儲存字串,存取物件或陣列時須搭配 JSON.stringify() 與 JSON.parse(),並做好空值與例外防呆:

// 1. 儲存物件資料(需先透過 JSON.stringify 序列化為字串)
const userSettings = { theme: "dark", notifications: true };
localStorage.setItem("userPreferences", JSON.stringify(userSettings));

// 2. 讀取資料並轉回物件(初次造訪可能為 null,需提供預設值並搭配 try...catch 防呆)
let savedSettings = { theme: "light", notifications: false };
try {
  const raw = localStorage.getItem("userPreferences");
  if (raw) {
    savedSettings = JSON.parse(raw);
  }
} catch (err) {
  console.warn("解析 localStorage JSON 失敗:", err);
}
console.log(savedSettings.theme); // 輸出: dark

// 3. 移除特定鍵值的資料
localStorage.removeItem("userPreferences");

// 4. 清空當前同源網域下的所有 localStorage 資料
localStorage.clear();

3. sessionStorage

sessionStorage 同樣屬於 Web Storage API,其操作方法與 localStorage 完全一致,但兩者的生命週期與分頁隔離機制截然不同。

sessionStorage 的資料僅在當前單一瀏覽器分頁(Top-level Browsing Context)的存活期間有效:

  • 在同一個分頁內重新整理(F5)或同源頁面跳轉時,資料會持續保留。
  • 一旦使用者關閉該分頁,資料就會立即被瀏覽器清空。
  • 即使使用者在同一瀏覽器中再開一個相同網址的新分頁,兩個分頁之間的 sessionStorage 也是各自獨立、互不共享的。
// 1. 儲存單一分頁內的表單暫存資料
sessionStorage.setItem("draftFormContent", "這是一段尚未送出的表單草稿");

// 2. 讀取暫存資料
const currentDraft = sessionStorage.getItem("draftFormContent");
console.log(currentDraft);

// 3. 表單成功送出後,移除該筆暫存鍵值
sessionStorage.removeItem("draftFormContent");

三、實際開發中的典型應用場景

了解三者的底層特性後,在實際專案架構中可依以下原則選擇適合的儲存位置:

  • Cookie 的適用場景:
    • 身分驗證與工作階段管理(Authentication & Session):由後端寫入加上 HttpOnly; Secure; SameSite 的 Session ID 或 Refresh Token,讓瀏覽器在每次 API 請求時自動帶上憑證,同時防範前端 XSS 腳本竊取。
    • 伺服器端渲染(SSR)需預先讀取的偏好狀態:例如 Next.js / Nuxt 等 SSR 架構需要在伺服器端收到第一次 HTML 請求時就知道使用者的語系(Locale)或深淺色主題,避免畫面閃爍(FOUC)。
  • localStorage 的適用場景:
    • 跨分頁持久化介面設定(UI Preferences):保存無涉機密的個人化設定,例如深色模式開關、側邊欄收合狀態、表格每頁顯示筆數等。
    • 非敏感靜態資料快取與離線草稿:快取變動頻率低的字典檔、選單設定或長篇編輯器自動備份草稿,減少重複 API 請求。
  • sessionStorage 的適用場景:
    • 多步驟表單或精靈流程(Multi-step Wizard):在分步驟填寫的結帳或註冊流程中暫存各步驟欄位值,重新整理時不會遺失,且關閉分頁即自動銷毀,不會殘留過期髒資料。
    • 單一分頁的列表篩選與捲動位置狀態:記錄使用者在某個分頁內的搜尋條件與閱讀位置,且不干擾使用者同時開啟的另一個分頁。

四、實務踩坑、注意事項與排查重點

在實際專案開發中,若將所有資料一律塞進 localStorage 或不當塞入 Cookie,極易引發資安漏洞或效能瓶頸。以下整理五大常見盲點與排查步驟:

1. 敏感機密資料誤存於 localStorage 的資安風險

  • 問題原因:localStorage 與 sessionStorage 內的資料可被同源頁面上的任何 JavaScript 直接讀取。若將密碼、個人隱私個資或高權限的長期存取憑證(Token)明文存入 localStorage,一旦網站存在跨站腳本攻擊(XSS)漏洞(或載入遭汙染的第三方套件),攻擊者僅需一行 localStorage.getItem() 即可將憑證盜走。
  • 解法與檢查步驟:
    • 原則:絕不將高敏感個資或長期認證憑證直接存放在 localStorage / sessionStorage。
    • 防禦寫法:身分驗證憑證應交由後端透過 Set-Cookie 標頭設定,並強制加上 HttpOnly(禁止 JS 讀取)、Secure(僅限 HTTPS 傳輸)與 SameSite=Strict 或 Lax(防範 CSRF 跨站請求偽造)。

2. Cookie 隨 HTTP 請求夾帶造成的網路頻寬浪費

  • 問題原因:每次瀏覽器向同網域發起 HTTP 請求(包含 API 呼叫、HTML 與同網域靜態資源)時,該網域路徑下的所有 Cookie 都會自動夾帶在 Cookie Request Header 中上傳。若開發者將大型 JSON 字串或冗長狀態塞進 Cookie,會直接導致每個請求標頭膨脹(甚至觸發伺服器 HTTP 431 Request Header Fields Too Large 錯誤),拖慢整體連線速度。
  • 解法與檢查步驟:
    • 原則:Cookie 僅存放極輕量且伺服器確實需要讀取的識別資訊,單筆與總量應控制在數百 Bytes 至數 KB 內。
    • 檢查步驟:開啟瀏覽器開發者工具(F12)切換至 Network 分頁,點選任一 API 請求並查看 Request Headers 中的 Cookie 欄位大小;純前端使用的狀態一律改存 localStorage 或 sessionStorage。

3. sessionStorage 在多分頁獨立運作的預期落差

  • 問題原因:不少開發者誤以為 sessionStorage 在同一個瀏覽器視窗的所有分頁中會即時共用。實際上,sessionStorage 是以單一分頁(Tab)為隔離單位;即使透過 <a target="_blank"> 或 window.open() 開啟同源新分頁時會複製當下那一刻的 sessionStorage 快照,兩個分頁後續對 sessionStorage 的修改仍是完全獨立、互不同步的。
  • 解法與檢查步驟:
    • 原則:若業務邏輯要求「使用者在分頁 A 變更狀態後,分頁 B 也必須同步反映」(例如購物車商品清單、全站登入/登出狀態),切勿使用 sessionStorage,應改用 localStorage 搭配 storage 事件或 BroadcastChannel API。

4. localStorage 超過容量上限(QuotaExceededError)的例外處理

  • 問題原因:localStorage 在多數瀏覽器中的同源容量上限約為 5MB。當程式無節制寫入大量快取資料(例如塞入 Base64 圖片或龐大 API 回應)超出上限,或在某些舊版瀏覽器無痕/隱私設定下遭到限制時,呼叫 setItem() 會直接拋出 QuotaExceededError(DOMException)。若未以 try...catch 攔截,將導致前端腳本中斷、畫面卡死。
  • 解法與防呆寫法:封裝寫入函式時務必加上 try...catch 攔截 QuotaExceededError,並在空間不足時主動清理舊快取或降級處理:
    function safeSetLocalStorage(key, value) {
      try {
        localStorage.setItem(key, JSON.stringify(value));
        return true;
      } catch (e) {
        if (e instanceof DOMException && e.name === "QuotaExceededError") {
          console.warn("本地儲存空間已達上限,正在清理舊快取...");
          localStorage.removeItem("oldCacheKey");
        }
        return false;
      }
    }

5. 跨分頁 storage 事件的狀態同步盲點

  • 問題原因:當使用者同時開啟同網站的分頁 A 與分頁 B 時,若在分頁 A 修改了 localStorage(例如切換主題或登出),分頁 B 的記憶體狀態不會自動更新,容易造成多視窗操作狀態不一致。
  • 解法與防呆寫法:可透過監聽 window.addEventListener('storage', ...) 事件實作跨分頁狀態同步。需注意:storage 事件只會在「非當前修改分頁」的其他同源分頁觸發,當前執行 setItem() 的分頁本身不會觸發該事件:
    window.addEventListener("storage", (event) => {
      if (event.key === "userPreferences" && event.newValue) {
        console.log("偵測到其他同源分頁修改了設定:", JSON.parse(event.newValue));
        // 同步更新當前分頁的 UI 狀態
      }
    });

總結

總結來說,Cookie、localStorage 與 sessionStorage 各有明確的職責邊界:需要隨 HTTP 請求送往伺服器驗證或搭配 HttpOnly 保護的敏感憑證交給 Cookie;需要跨分頁長期保留的非敏感前端偏好設定交給 localStorage;而僅限單一分頁流程暫存、關閉分頁即應銷毀的表單或檢視狀態,則交給 sessionStorage。依資料機密性、生命週期與作用域精準分工,才能兼顧前端體驗與系統資安。

參考資料與延伸閱讀

發表迴響