Extensions often need to store something: settings, tokens, user data. The easy path is to dump it in localStorage and move on, but extensions handle sensitive data and run in a shared browser, so where and how you store matters more than in a normal web app. Here is how I keep Chrome extension storage secure, and what never belongs in it.

Use chrome.storage
chrome.storage is the extension-native option: accessible from the background worker and popup, able to sync across the user’s devices if you want, and the correct home for extension data. Plain localStorage is tied to a single context and isn’t the right tool here. It is not even available in the background service worker, which is where much of an extension’s logic runs.
There are three storage areas, and choosing the right one is the first security decision:
chrome.storage.localis saved on the device and survives restarts. It suits most settings and cached data.chrome.storage.syncfollows the user across their signed-in browsers, with tight size limits. Keep it for preferences, never for tokens.chrome.storage.sessionis held in memory and cleared when the browser closes. By default content scripts cannot read it, which makes it the better place for short-lived sensitive values.
One point people miss: the chrome.storage documentation states that the storage area is not encrypted. It is the right place for extension data, but it is not a vault. Anything stored there should be something you could live with being read by someone who has the device.
Never ship secrets you don’t have to
The biggest mistake is bundling an API key inside the extension. Anyone can unpack an extension and read its code, so a bundled key is a public key. If your extension talks to a paid API, route those calls through your own backend that holds the key, don’t embed it.
That backend can be small: one endpoint that checks who is calling, applies a rate limit, and forwards the request with the real key attached. If the extension pairs with your business website, that site is often the natural home for it. A WordPress site can expose it through its REST API, while a hosted site builder may not let you run server code at all, which is one of the trade-offs I cover in WordPress vs Webflow for a small business.
Treat user tokens carefully: scope them tightly and expire them, because a token that lives forever in storage is a standing risk if the device is compromised. And request the minimum permissions. Every permission is attack surface and a reason for users and reviewers to distrust the extension.
Handle tokens like they will leak
Use short-lived access tokens and refresh them through your backend. For sign-in with a third-party provider, chrome.identity handles the OAuth flow so you never see the user’s password. When the user logs out, remove their tokens and cached data from storage straight away, and never write a token to the console, even while debugging.
A security checklist for Chrome extension storage
Run through this before every release. Each item is something a reviewer or an attacker can check in minutes.
- Search the built bundle for keys. Unpack your own release and search for anything that looks like an API key or secret.
- List what you store and why. If you cannot say why a value is kept, delete it.
- Keep sensitive values out of content scripts. Content scripts run inside web pages you do not control. Let the background worker hold tokens and do the authenticated calls.
- Validate every message. Treat messages from content scripts as untrusted input, and check the sender and the data before acting on them.
- Trim permissions. Move rarely used ones to optional permissions requested at the moment they are needed. The Chrome guide to declaring permissions explains how.
- Test logout. After logging out, open the storage viewer in DevTools and confirm nothing personal is left behind.

Common storage mistakes I see
- Treating obfuscation as protection. Minifying or encoding a key does not hide it. Anyone patient can recover it.
- Storing full user records “just in case”. Cache only what the interface needs, and fetch the rest when required.
- Keeping cached data forever. Give cached personal data an expiry time and clear old entries on a schedule with
chrome.alarms, so it does not quietly pile up on the device for years. - Putting tokens in sync storage. That copies them to every browser the user signs into.
- Trusting the page. Data read from a website’s DOM can be manipulated by that website, so check it before storing it.
The short version
Store data in chrome.storage, keep real secrets on your server, scope and expire tokens, and ask for as little access as possible.
Storage is only one part of getting through review. Broad permissions and remote code are the other usual problems, covered in common Manifest V3 mistakes that get extensions rejected. If you are building the interface in React, building a Chrome extension with React shows how storage fits into the popup. And if you would like the extension and its backend built properly from the start, that is part of my web application development work.




