Chrome Web Store 公開ガイド
拡張機能の公開準備とストア提出プロセス。
公開前チェックリスト
必須項目
- マニフェストの
name,version,descriptionが正確 - アイコン(16x16, 48x48, 128x128 px)が用意されている
- プライバシーポリシーが用意されている(ユーザーデータを扱う場合)
- 権限が最小限に設定されている
- 動作テストが完了している
推奨項目
- Capture from the packaged runtime with synthetic data in an isolated, sync-disabled profile verified empty before seeding. Await ready state plus expected records/controls, not nonempty status text; inspect every saved image and its required dimensions (1280x800 or 640x400). Obsolete selectors or cleanup failure fail the capture run.
- プロモーションタイル画像(440x280 small、920x680 large)
- 詳細な説明文(多言語対応推奨)
- カテゴリが適切に選択されている
Release Gate
- Before edits, read current remote tags/releases and the public store version; old pending-review notes are not current evidence. Use a new version for changed runtime content, never move a published tag or overwrite its verified ZIP.
- Run the repository's declared test/lint/typecheck/build/package gates; do not require nonexistent scripts such as a bridge validator. Check dependencies when present, distinguishing runtime from dev-only findings and avoiding unrelated major upgrades. Tie package/manifest versions and submission copy to the same release; remove stale "unreleased" feature/privacy claims before publication.
- Verify the actual ZIP's manifest version, entry allowlist/count, size and SHA256; compare extracted runtime bytes with the intended build and test that extracted package. Exclude credentials, private data, local customization and temporary profiles. A source-tree test or CLI success line alone does not validate the shipped artifact.
- Verify remote commit/tag refs and Release assets independently, using asset digests when available. Track GitHub publication, store upload/draft version, submission acceptance, review and public availability separately. Read back saved descriptions and screenshot thumbnails after navigation; disabled Save alone is not persistence proof.
- If store authentication blocks after GitHub publication, preserve the same verified artifact, commit/tag and prepared listing assets. Report the exact unfinished stage and resume it after publisher/item verification; do not bump/rebuild/republish or resubmit solely because a public page is stale. API client credentials alone do not prove a usable authorized API session; never print secrets while checking availability.
- A provider's unsafe-browser sign-in rejection is not proof of account mismatch or CDP causation. Stop unchanged-route retries, request a supported normal-browser login check, and keep secrets user-entered. Manual login does not grant automation access; use a verified authorized route or manual submission, without stealth flags, credential copying or unapproved browser restarts. Google sign-in guidance.
ZIP パッケージ作成
WXT の場合
# プロダクションビルド
npm run build
# ZIP 生成
npm run zip
# → .output/[name]-[version]-chrome.zip が生成される手動の場合
# PowerShell
Compress-Archive -Path ".output/chrome-mv3/*" -DestinationPath "extension.zip"Chrome Web Store Developer Dashboard
アカウント設定
- Chrome Web Store Developer Dashboard にアクセス
- 初回は $5 の登録料が必要
- デベロッパーアカウントを設定
新規アイテム追加
- 「新しいアイテム」をクリック
- ZIP ファイルをアップロード
- ストアリスティング情報を入力:
- 詳細説明
- カテゴリ
- 言語
- スクリーンショット
- プライバシーポリシー URL
権限の正当化
ストア審査では、使用する各権限の正当化が求められる。
| 権限 | 正当化例 |
|---|---|
tabs |
タブのURL/タイトルを表示するため |
<all_urls> |
すべてのウェブサイトでコンテンツスクリプトを実行するため |
storage |
ユーザー設定を保存するため |
cookies |
ログイン状態を確認するため |
activeTab |
ユーザーがクリックした時のみ現在のタブにアクセスするため |
審査プロセス
審査期間
- 通常: 1〜3 営業日
- 複雑な権限を使用する場合: 1〜2 週間
よくあるリジェクト理由
| 理由 | 対策 |
|---|---|
| 権限の過剰要求 | 必要最小限の権限に変更 |
| プライバシーポリシー不備 | ユーザーデータの取り扱いを明記 |
| 機能の説明不足 | ストア説明を詳細化 |
| リモートコード | すべてのコードをバンドルに含める |
| 誤解を招く説明 | 正確な機能説明に修正 |
| 低品質のUI | UIを改善 |
Dashboard の既定値の罠
- Privacy タブの remote code ラジオが 「使用している」側に選択済みで到着することがある。 remote code を持たない拡張でも、触らずに submit すると事実と逆の申告になる。 意図した値を明示的に選び直し、保存後に読み戻して検証する。他のラジオ群も同様に空とは限らない。
- Classify each data checkbox from implemented features, stored fields, payloads and listeners, not manifest names or incidental page content. Local-only handling counts: an optional encrypted card number/expiry is financial/payment data even without model transfer. Align that category, the store listing, permission justifications and the public privacy policy before upload. A requested click is not activity logging; a typed address is PII, not device geolocation. Disclose limits for arbitrary page text, images and attachments without claiming dedicated collection. Source: https://developer.chrome.com/docs/webstore/program-policies/user-data-faq.
- 保存後はページを再読込し、全データ種別、Limited Use の全証明、remote code、単一目的、権限理由、policy URL を構造化して読み戻す。保存ボタンの disabled だけを永続化の根拠にしない。申告と公開ポリシーと実装の不一致は公開ブロッカー。
- 「送信できない理由」系のバナーは、必須項目をすべて埋めた後も残り、ダイアログ本文が空のことがある。 実障害と断定する前に一度 draft を保存し直し、それでも残るかで判定する。
Microsoft Edge アドオンとの差分
同じ MV3 ZIP をそのまま提出できるが、掲載アセットの仕様がずれている。特に 640 系は互換ではない。
| 項目 | Chrome Web Store | Edge アドオン |
|---|---|---|
| スクリーンショット | 1280x800 または 640x400、最大 5 枚 | 1280x800 または 640x480、最大 6 枚 |
| ストアロゴ | 128x128 | 300x300 推奨(最小 128x128、比率 1:1) |
| 詳細な説明 | 上限のみ | 最小 250 文字、最大 10,000 |
| 言語ごとの必須 | 説明のみ | 説明とロゴが各言語で必須(名前と短い説明は 1 言語以上) |
1280x800 で撮れば両方を満たす。640 で撮ると片方でしか使えない。ロゴを 128x128 だけで用意すると Edge では最小要件ぎりぎりになる。日本語ロケールを足す作業量は、説明だけの Chrome より Edge の方が大きい。
プライバシー申告は現行の Edge Privacy ページが Chrome とほぼ同型で、単一目的 / 権限ごとの正当化 / リモートコード宣言 / データ種別の開示と証明 / ポリシー URL が揃う。旧 UI は Properties ページ上の 「個人情報にアクセス・収集・送信するか」の Yes/No + URL だけだったので、どちらの画面が出るかを実物で確認する。 MV3 はリモートコード自体が不可だが、宣言欄は存在する(「不可だから欄がない」は誤り)。
旧 UI の Yes/No で「いいえ」を選んでも、後から個人情報を処理すると判断されれば認定に失敗しうると 公式が明記している。ブックマークや閲覧関連データを読む拡張は、いいえを選ぶ場合でもポリシー URL を併記する。
出典: https://learn.microsoft.com/ja-jp/microsoft-edge/extensions/publish/publish-extension
商標セーフな命名 (公開後の takedown 対策)
審査通過後でも、商標権者の代理 (例: Microsoft 代理の Tracer microsoft@tracer.ai) が
Google 経由で商標侵害を申し立てると、7 日以内に是正しないと item が suspend される。
- 苦情が狙うのは item の Title に当たる箇所: manifest
name/action.default_title/ ストアリスティング名。ここから他社商標 (GITHUB, Copilot, Microsoft 等) を外す。 description/ README / keywords での nominative な互換性言及 は許容 (例:Works with GitHub Copilot or local LLMs)。エスカレートしたら description も中立化するが、 Title 修正だけで suspend リスクは消える。- 商標対応で manifest
nameの内部 ID や設定キー prefix を変えない — 既存ユーザーのインストール / 設定が壊れる。変えるのは人間が見る Title 文字列だけ。 - リネーム後は全面 grep して、ビルド生成物 (
.outputの manifest、コンパイル済み JS) からも 旧 Title が消えていることを確認する。 - ZIP 反映と リスティング項目は別物。Title だけ直して submit しても、CWS の 説明文の見出し / Privacy policy URL / Store icon / Screenshots 画像 / Website・Support URL に旧名が残ったままだと再申し立ての火種になる。再申請前に Dashboard 上で全項目を点検する。 Screenshots は画像内に写り込んだタブ title やパネル見出しも対象。新名で撮り直して差し替える。
- ストアの URL slug (
/detail/<slug>/<itemId>) は Title 由来で 公開後に自動再生成される (item ID は不変、旧 URL は redirect で生き続ける)。slug を直接変更する API は無いため、 Title 中立化 + 審査通過後に新 slug を検証する。 - Dashboard の Screenshots スロットを自動操作する場合、削除はサムネイル hover で出るボタン、
追加は drop zone の click →
expect_file_chooser経由が確実。input[type=file]への 直接set_input_filesは隠し input に当たって反映されないことがある。 - ただし UI 部品ごとに動作が違う: Store icon (128×128) は
input[type=file]への直接set_input_filesが通る。Screenshots だけ drop zone + file chooser 必須。アップロード後は 必ず thumbnail / preview の有無で反映を検証する (SCREENS_AFTER_UPが増えない=失敗)。 - 上記は selector ベースの
set_input_filesを使った場合の話。raw CDP のDOM.setFileInputFiles(Runtime.evaluateで Element をreturnByValue: falseで取り、objectId→DOM.requestNode) では、shadow DOM 内の input に対して icon も screenshots も送れ、保存後も残った実例がある。 どちらも確実とは言えないので代替経路として扱い、経路に関係なく「保存後の thumbnail 数」で検証する。 - Screenshots は
24 ビット PNG(アルファなし)が要件。生成ごとに PNG の IHDR (bit depth 8 / colour type 2 = RGB、2 以外の 6 = RGBA)を検査し、RGBA なら変換する。 不透明なページをPage.captureScreenshotした場合は RGB になった実例があるが、常にそうとは限らない。 - hover overlay を出すときは sticky header が pointer を奪う。
scroll_into_view_if_neededとwindow.scrollByで対象を viewport 中央 ~300px 下へ移動し、座標ベースのpage.mouse.move(cx, cy)で hover してから click すると安定する。 - ダッシュボードを撮ったスクショには publisher email / extension ID が写る。
証跡として残す画像(メール添付用など)は repo の
store-assets/evidence/に隔離し、 そのパスを.gitignoreに追加して public repo に絶対に commit しない。
アップデート公開
バージョン更新
// wxt.config.ts
export default defineConfig({
manifest: {
version: "1.1.0", // バージョンを更新
},
});更新手順
npm run zipで新しいZIPを生成- Developer Dashboard で該当アイテムを選択
- 「パッケージ」タブで新しいZIPをアップロード
- 変更履歴を入力
- 送信して審査を待つ
Artifact Hygiene
- ZIP の中身を列挙し、
src/,tests/,.github/,.vscode/,store-assets/,*.map, log が入っていないことを確認する。ロゴやタイルなどの掲載専用画像も同様で、パッケージ内に置かない。manifest が参照しないファイルを弾く readiness checker を使っている場合は、そこで落ちる。 - OAuth
invalid_grantは refresh token 失効として扱い、同じ ZIP を保持したまま再認可する。廃止済み OOB redirect を使わず、通常ブラウザーの承認と state + PKCE 付き loopback callback を使う。auth code / refresh token はチャットやログへ貼らず、更新後は token exchange と item read の成功だけ確認する。 .env.submit/.env*/ token / client secret / auth code を grep や全文検索の対象にしない。存在確認、キー名確認、dry-run 成功、CWS API のcrxVersion/itemErrorだけを証跡にする。- live retry 前に利用中 CLI の package/repository/help を確認する。手順書の旧 CLI 名や存在しない dry-run option を推測で実行せず、upload と publish が分離できる CLI では upload → DRAFT 版確認 → publish の順にする。
- CWS publish が止まった場合でも、ZIP と SHA256 を GitHub Release に残して再開可能にする。
- 審査中、認証、権限、duplicate など外部状態で publish だけ止まる場合は、ZIP / SHA256 / version / commit / tag / upload の状態を分けて記録し、再開条件を明記する。
- ブロッカー解消後の再開では、最新タグの ZIP を rollup 提出する。ブロック時点の古い ZIP を蘇生せず、間に積まれた patch をまとめて出す(実例: v0.1.11→v0.1.15 で 4 版分を一括公開)。
publish-extensionが400 "Publish condition not met: You may not edit or publish an item that is in review."で失敗する場合は、前回の draft が審査キュー残留中。先に?projection=DRAFTで stuck しているcrxVersionを確認し、Dashboard UI からmore_vert→ 審査をキャンセル → 確認ダイアログで取り下げる。ステータスバッジが「公開済み」に戻り次第publish-extensionを再実行できる。- 同じ制限は UI 側にも出る。審査中は保存と提出の操作が無効化され、掲載情報を一切編集できない。解除条件は「審査完了」か「審査キャンセル」の 2 つだけ。ロケール追加や文言修正を提出直後にやる前提で計画しない。先に入れるか、次の update に回す。
- publish 後の CWS API 確認は item endpoint に
?projection=DRAFTを付ける。crxVersionが対象版でitemErrorが 0 件なら API 側の確認は通過扱いにし、uploadState: NOT_FOUNDだけで失敗判定や再アップロードをしない。 itemErrorが null 要素だけを返すことがあるため、配列長だけで失敗判定しない。API 応答が疎または stale な場合、CLI の upload/publish 成功、DRAFT の対象版、Dashboard の package/status を突合する。- Submit UI のクリック結果が曖昧なら同じ click を反復しない。Dashboard status が未適用と確認でき、認証済み API publish が使える場合だけ 1 回切り替える。完了は API の Pending review と Dashboard の「審査待ち」を両方確認し、公開中の旧版とは別状態として記録する。
自動パブリッシング
WXT Auto-Publishing
# Chrome Web Store API を設定
npm install -D chrome-webstore-upload-cli
# 環境変数設定
export EXTENSION_ID="your-extension-id"
export CLIENT_ID="your-client-id"
export CLIENT_SECRET="your-client-secret"
export REFRESH_TOKEN="your-refresh-token"
# アップロード&公開
npx chrome-webstore-upload upload --source .output/*-chrome.zip --auto-publishGitHub Actions 自動公開
# .github/workflows/publish.yml
name: Publish to Chrome Web Store
on:
release:
types: [created]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npm ci
- run: npm run build
- run: npm run zip
- name: Upload to Chrome Web Store
uses: mnao305/chrome-extension-upload@v5.0.0
with:
file-path: .output/*-chrome.zip
extension-id: ${{ secrets.EXTENSION_ID }}
client-id: ${{ secrets.CLIENT_ID }}
client-secret: ${{ secrets.CLIENT_SECRET }}
refresh-token: ${{ secrets.REFRESH_TOKEN }}非公開配布
開発者モード(ローカル)
chrome://extensionsを開く- 「デベロッパーモード」を有効化
- 「パッケージ化されていない拡張機能を読み込む」
- ビルドフォルダを選択
CRX パッケージ(社内配布)
# Chrome で CRX を生成
# chrome://extensions → パック拡張機能
# または
npx crx pack .output/chrome-mv3 -o extension.crx