Chrome拡張機能の開発入門|最小構成から公開まで
Chrome拡張機能の開発に手を出す人の多くは、拡張機能の開発者になりたいわけではありません。取引先の管理画面が1時間でログアウトする、毎日押すボタンが2階層下に埋まっている、別のタブにある値をこちらの画面へ持ってきたい。そういう具体的な不満があって、それを直す最短経路として拡張機能に辿り着きます。仕組み自体は半日で把握できる規模です。つまずくとすれば、いま検索で出てくる解説の相当数が、もう動かない世代の書き方を説明している点です。
2026年に入門するなら、覚える版は1つだけ
最初に確認すべきは、参照している資料がどの世代のものかです。古いほうの形式は完全に終わっています。Chromeの公式資料は、その終着点をこう記しています。
残りの Manifest V2 拡張機能がすべて Chrome ウェブストアから削除されます。Chrome 138 以前にインストールされた Manifest V2 拡張機能は引き続きインストールされますが、更新を受け取ることができず、Chrome から削除された後に Chrome ウェブストアから再インストールすることはできません。 出典: developer.chrome.com
同じ資料には、Chrome 138ですべてのチャンネルの全ユーザーに対してManifest V2が無効になり、ユーザーが再び有効にすることはできなくなった、とも書かれています。日付は2025年7月24日です。
ここから実務上の結論が2つ出ます。1つ目は、manifest の宣言が2で始まる解説、背景ページを常駐させる書き方を紹介している解説は、少し古いのではなく読み込めないということ。2つ目は、古い形式のコードは版数を書き換えるだけでは動かないということです。背景で動かす部分の仕組みそのものが入れ替わっているためです。
見分けは簡単です。background.page や persistent が出てくる、chrome.browserAction を呼んでいる、webRequest で通信を止めている。このいずれかがあれば古い世代の資料です。考え方の参考にはなりますが、そのまま写経すると動きません。
いちばん小さい拡張機能の中身
拡張機能の実体はフォルダです。Chromeはそのフォルダを読み込むだけで、必須のファイルはマニフェスト1枚しかありません。
{
"manifest_version": 3,
"name": "受信箱へ飛ぶ",
"version": "1.0",
"action": { "default_popup": "popup.html" },
"background": { "service_worker": "background.js" },
"permissions": ["tabs"]
}
この横に置けるコードは大きく4種類あり、どれがどこで動くのかを最初に押さえると迷いが減ります。
| 部品 | 動く場所 | よく使う用途 |
|---|---|---|
| ポップアップ | 拡張機能が持つ小さなページ | ツールバーのアイコンを押したときのボタンや設定 |
| Service Worker | 拡張機能専用の短命な実行環境 | イベントへの反応、API呼び出し、状態の保存 |
| コンテンツスクリプト | 閲覧中のページの内部 | そのサイトのDOMを読む、書き換える |
| オプションページ | 拡張機能が持つ通常のページ | ポップアップに収まらない設定画面 |
境界として重要なのは、コンテンツスクリプトとそれ以外の間です。コンテンツスクリプトはページのDOMを共有しますが、ページ側のJSの変数は共有しません。呼べる拡張機能のAPIも限られていて、それ以外はService Workerへメッセージを送って代行してもらう形になります。ページの変数を直接読もうとして値が取れないという相談はこの境界が原因で、回避するにはページ側にスクリプトタグを差し込み、カスタムイベントでやり取りします。
コンテンツスクリプトは、マニフェストの matches にURLの並びを書いて、どのページで動かすかを指定します。ここは最初から狭く書く価値があります。すべてのサイトに一致する書き方をすると、閲覧のたびに読み込みが挟まるうえ、インストール時の警告も最も広いものになるためです。社内の1つの画面でしか使わないなら、そう書きます。コンテンツスクリプトが差し込む画像やCSSのように、ページ側から読み込ませるファイルがある場合は web_accessible_resources に別途登録が要ります。登録を忘れるとコンソールにファイル名だけのエラーが出るので、原因に気づきにくい部類です。
権限は、動かなくなってから足すのではなく最初のマニフェストに書きます。書いた内容が、コードにできることと、利用者に出る警告文の両方を決めるためです。
公開せずに自分のChromeへ入れる
作ったものを使うのに、ストアへの公開は要りません。公式のチュートリアルが示す手順はこうです。新しいタブで chrome://extensions を開き、デベロッパーモードのスイッチを入れ、「展開済みを読み込む」から拡張機能のフォルダを選ぶ。
これで、読み込んだプロファイルの中では通常のインストールと同じように動きます。削除するまで残り、Chromeを再起動しても消えず、ツールバーに固定もできます。自分の不満を1つ直すための道具なら、配布の話はここで完結します。
初心者が時間を落とすのは、コードを直したあとの反映です。反映のさせ方が部品ごとに違います。
- マニフェストとService Workerを直したとき: 拡張機能のカードにある再読み込みのアイコンを押す
- コンテンツスクリプトを直したとき: 再読み込みに加えて、対象のページも読み込み直す
- ポップアップとオプションページを直したとき: どちらも不要。開くたびに読み直されるため
読み込みについて、後から効いてくる点が2つあります。1つは、拡張機能はそのとき開いていたプロファイルに入るということです。同じMacの別のプロファイルからは見えないので、そちらでも使いたければ同じフォルダをもう一度読み込みます。もう1つは、開発中に読み込んだ拡張機能のIDが常に同じとは限らないことです。保存したデータはIDに紐づくため、IDが変わると設定が空に戻ります。公式の資料はマニフェストの key フィールドについて、開発中に1つのIDを保ち続けることは不可欠だと説明しています。サーバーと通信する、他から呼ばれる、ページにファイルを見せる、のいずれかに当てはまるなら早めに設定してください。
デバッグの入り口も分かれています。ポップアップは右クリックの「検証」で通常の開発者ツールが開きます。Service Workerは拡張機能のカードにある専用のリンクから別の開発者ツールが開きます。開発者ツールを開いていない間に出たエラーは、カードの「エラー」から後から読めます。
Service Workerは常駐しない
古い解説との最大の違いがここです。背景で動くコードは、待機し続けてくれません。Chromeの資料は終了の条件をこう説明しています。
30 秒間操作がない場合。イベントを受信するか、拡張機能 API を呼び出すと、このタイマーがリセットされます。イベントや API 呼び出しなどの単一のリクエストの処理に 5 分以上かかる場合。fetch() レスポンスの到着に 30 秒以上かかる場合。 出典: developer.chrome.com
終了すると、そこで持っていたグローバル変数はすべて消えます。件数をモジュール直下の変数で数える書き方は、イベントが続けて届く開発中はうまく動き、実際に使い始めてから静かに0に戻ります。残したい値は chrome.storage に置きます。ローカル領域なら終了にもブラウザの再起動にも耐えます。
イベントの登録位置にも決まりがあります。リスナーはファイルの最上位で登録します。コールバックの中や非同期関数の中で登録すると、休止状態から起こされたときに間に合いません。Chromeはイベントが届いた時点でService Workerを起動し、すぐにリスナーを探すためです。
時間待ちも注意が要ります。数秒を超える setTimeout は、発火する前にService Workerが終了している可能性があるため当てになりません。この用途には chrome.alarms が用意されていて、こちらはService Workerを起こしてから通知します。
権限の書き方で、入れてもらえるかが決まる
権限の一覧は、利用者が実際に読む数少ない情報です。積み上がった結果ではなく、設計して決める部分だと考えてください。
すべてのURLに一致する記述で広いホスト権限を要求すると、インストール時に「すべてのウェブサイト上のデータの読み取りと変更」という警告が出ます。この一文で入れるのをやめる人はかなりの割合にのぼりますし、会社の端末では管理者の設定で弾かれる理由にもなります。
狭くする道は用意されています。host_permissions に対象のドメインだけを列挙すれば、警告の文面も届く範囲も狭まります。activeTab は、利用者が拡張機能のアイコンを押した瞬間だけ現在のタブへアクセスできる仕組みで、インストール時の警告が出ません。小さな道具の多くはこれで足ります。必須ではない権限は optional_permissions に分けて、必要になった場面で要求すれば、初回の印象を悪くせずに済みます。
自分だけが読み込んで使う道具なら、ここはコードの動作に影響しません。効いてくるのは、2人目に「入れてみて」と頼む瞬間からです。
拡張機能に許されていない範囲を先に知る
天井を先に把握しておくと、実現できない形を1週間追いかけずに済みます。
拡張機能はプロファイル単位でインストールされます。別のプロファイルを覗くことも、別のプロファイルのクッキーを読むこともできません。よくある要望である「同じサービスのアカウントを2つ同時に開きたい」は、そもそも拡張機能で解く問題ではないということです。
同じプロファイルの中に2つ目のセッションを作ることもできません。クッキーはプロファイルの保存領域に属しているので、1つの保存領域には1サイトにつき1つのログインしか置けません。保存領域を分ける仕組みはブラウザ本体が持つもので、拡張機能を上に載せて実現できるものではありません。
ブラウザの窓の構造も変えられません。タブはタブのままで、Webアプリを独立した窓に変えて、Dockに固有のアイコンを置き、通知の出どころを分けるといったAPIは存在しません。この分離はブラウザ側の設計に属していて、アプリごとに独立したワークスペースという考え方で最初からそう作られているのがアプリ集約ブラウザです。仕事の単位で窓とセッションをまとめる仕組みはワークスペースにまとめられており、その形で使えるサービスの一覧は使えるアプリにあります。
公開するか、しないかを分けて考える
公開は、作ることとは別のプロジェクトです。ストアに出すには開発者としての登録が必要で、公式の資料には登録時に1回限りの登録料を支払うと書かれています。掲載には名前、説明文、スクリーンショット、アイコン、利用者のデータを扱うならプライバシーポリシー、そして要求する権限それぞれの理由が要ります。
審査には時間がかかり、要求している権限によって長さが変わります。更新のたびに同じ審査を通ります。社内だけで使う道具の場合、この手間は割に合わないことが多く、選択肢は3つあります。展開済みの読み込みのまま使い続ける、限定公開でリンクを知っている人だけが入れられるようにする、組織が管理している端末なら管理者の設定で配布する、の3つです。
最初に作るものを1つに絞る
入門として効率がよいのは、いま感じている不満のいちばん小さい断片を選ぶことです。ポップアップにボタンを1つ置き、押したら決まったページを決まったタブで開く。これだけで、マニフェスト、アクション、タブのAPI、再読み込みの手順が1時間で身につきます。
そのうえで、その拡張機能が何を埋め合わせているのかを見てください。押しづらいボタンを1つ手前に出す用途なら、拡張機能は正しい道具です。ただし、原因が「1つの窓に多すぎるサービスとアカウントが同居していること」であれば、拡張機能は原因に届きません。プロファイルの境界もセッションの数も、拡張機能からは動かせない層にあるからです。同種の道具を検討する場合、機能の範囲と料金は製品によって差があるので、Waveboxとの比較のような並べ方で、自分が必要としている分離がどこまで含まれているかを確認しておくと判断が早くなります。
よくある質問
古いChrome拡張機能の解説記事は今でも使えますか?
Manifest V2を前提にしたものは使えません。2025年7月24日にChrome 138ですべてのユーザーに対して無効になり、2026年8月31日には残っていた拡張機能がウェブストアから削除されました。マニフェストの版数が2、背景ページを常駐させる書き方、chrome.browserAction の呼び出しが出てくる資料は読み込めない世代のものです。
作った拡張機能は公開しないと使えませんか?
公開は不要です。chrome://extensions でデベロッパーモードを有効にし、「展開済みを読み込む」からフォルダを選べば、そのプロファイルにインストールされた状態になります。Chromeを再起動しても残ります。公開が必要になるのは他人に配るときで、少人数なら限定公開という選択肢もあります。
Service Workerに入れた値が消えてしまうのはなぜですか?
30秒間何も起きないとService Workerが終了し、そのとき保持していたグローバル変数がすべて失われるためです。残したい値は chrome.storage のローカル領域に保存してください。数秒を超える待ち時間も setTimeout ではなく chrome.alarms を使うほうが確実です。
拡張機能で同じサービスに2つのアカウントを同時にログインできますか?
できません。拡張機能は1つのプロファイルの中で動き、クッキーはそのプロファイルの保存領域に属しているため、1サイトにつき1つのログインしか保持できません。2つ同時に持つには保存領域をもう1つ用意する必要があり、それはプロファイルを分けるか、ブラウザ本体がセッションを分ける仕組みを持っているかのどちらかです。