ブラウザに届くコードに秘密は置けない:公開前にAPIキーを探して置き場所を変える
AIを使いこなすClaudeで執筆し、GPTで自動校閲・修正しています。

結論から言うと、ブラウザに届くコードはアクセスした人が誰でも読めるので、「読まれたら困るキー」は一切置けません。置いてよいのは、公開されても被害が出ないように提供元の側で制限をかけられる種類のキーだけです。見分けのポイントは、そのキーが「身元を示すだけの識別子」なのか、「持っているだけで操作や課金ができる鍵」なのかです。 この記事では、公開前に検索する語句の一覧、キーの種類別の置き場所、流出に気づいたときの動きを順に示します。確認した公式資料は2026年10月28日時点のものです。サービスの設定画面や仕様は変わることがあるため、実際の操作では各社の最新の案内も見てください。
「公開してよいキー」と「置けないキー」の見分け方
環境変数に入れれば安全、というわけではありません。環境変数は「ソースコードの外に値を置く仕組み」にすぎず、ブラウザ向けのコードに取り込まれる設定にすれば、その値はビルド後のファイルに埋め込まれて誰でも読めます。ブラウザに出る変数の条件(名前の接頭辞など)はフレームワークごとに違うので、使っているものの文書で確認してください。
判断の基準は次の表です。編集部の整理で、各社の全製品を網羅したものではありません。
- Firebaseの設定に含まれるAPIキー、ブラウザのコードに置けるか:置いてよい、理由と条件:公式は、Firebaseサービス用のキーはコードや設定ファイルに含めてよいとしています。ただし、Firebase関連のAPIにだけ使う、API制限をかける、Security RulesとApp Checkで認可を整える、が条件です
- 地図などのブラウザ用キー、ブラウザのコードに置けるか:制限をかけた上で置く、理由と条件:Google Maps Platformのセキュリティ指針では、Webサイトには許可するドメインをHTTPリファラーで指定し、使うAPIだけを許可するとされています
- 生成AIのAPIキー(Geminiなど)、ブラウザのコードに置けるか:置けない、理由と条件:呼び出した人の課金につながります。Firebaseの文書も、Firebase用のキーにGemini APIなどを混ぜてはいけないと注意しています
- 決済や外部サービスの秘密鍵、DBの管理者権限キー、Webhookの署名用シークレット、ブラウザのコードに置けるか:置けない、理由と条件:持っているだけで操作できる鍵です。名前に「secret」「service role」などが付くものが典型です
- GitHubのトークン、各種アクセストークン、ブラウザのコードに置けるか:置けない、理由と条件:リポジトリや他人のデータを操作できる権限を持ちます
迷ったら「このキーを知らない第三者が、私のお金やデータを動かせるか」で考えます。動かせるなら置けないキーです。「公開してよい」と言われるキーも、制限や認可の設定が前提で、キーだけで安全になるわけではありません。
公開前に検索する語句の一覧
検索する対象は、ソースだけでなく、ブラウザに実際に届くファイル(ビルド成果物)です。ソースでは環境変数の名前しか見えず、値がビルドに埋め込まれたかどうかは成果物を見ないと分かりません。
語句の一覧
- 名前に関する語句:API_KEY、apikey、api_key、secret、token、password、private、service_role、Authorization、Bearer
- 値の形に関する語句:利用中のサービスのキーの書き出し(例:AIza、sk-、ghp_、github_pat_)。接頭辞は提供元ごとに違うため、自分が契約したサービスのキーの見本を管理画面で見て、頭の数文字を控えておきます
- 設定ファイルの名前:.env、.env.local、config.json、credentials、serviceAccount、.pem、.p12
- 通信先の語句:外部APIのURLを直接書いている箇所(fetch( や axios の周辺)
検索の手順(説明用の例で、実行して確認したものではありません)
ターミナルが使える場合の例です。dist はビルド成果物のフォルダ名の例で、お使いの環境に読み替えてください。
# 1. ソースと設定の確認(.git と node_modules は除く)
grep -rniE "api[_-]?key|secret|token|password|service_role|bearer " . \
--exclude-dir=.git --exclude-dir=node_modules
# 2. ビルド成果物の確認(ブラウザに届く側)
grep -rniE "api[_-]?key|secret|token|AIza|sk-|ghp_|github_pat_" dist
# 3. 履歴に過去のキーが残っていないかの確認
git log -p --all -S"AIza" --oneline3つ目の -S には、手元のキーの頭の数文字を入れます。ヒットした行は、すべて「公開してよいキーか」を上の表で判定します。
AIに見てもらうときの依頼文
AIに確認を手伝ってもらうなら、本物のキーは貼らず、値を伏せた状態で渡します。
次はAPIキーの検索結果です。値は「XXXX」に置き換えています。
各行について、(1)キーの種類の推測、(2)ブラウザのコードに置いてよいか、
(3)置けない場合にサーバー側へ移す方法、を表で示してください。
判断に必要な情報が足りない行は、推測せず「要確認」と書いてください。
(ここに、値を伏せた検索結果を貼る)人が確認する点は、AIの「種類の推測」が当たっているかです。キーの種類は管理画面で必ず自分で見て決めます。
種類別の置き場所
置いてよいキー:制限をかけて置く
Firebaseのキーは、Google Cloudコンソールの「APIs & Services > Credentials」でキーごとにAPI制限を設定できます(Firebaseの案内)。守りの本体はセキュリティルールとApp Checkで、キーを隠すことではありません。地図などのキーはHTTPリファラー制限とAPI制限をかけます。リファラーはプライバシー上の理由でブラウザが送らないことがあると、Googleの指針にも書かれています。課金に直結するAPIなら、利用上限やアラートの設定も併せて検討してください(これは編集部の提案です)。
置けないキー:サーバー側に移す
置けないキーは、自分のサーバー側の処理(サーバーレス関数など)に置き、ブラウザからはその処理だけを呼びます。ブラウザは「この操作をして」と頼むだけで、キーは見ません。
Vercelを使う場合、環境変数にはConfigとSecretの2種類があり、Secretは保存後に値を読み出せない書き込み専用になる、と説明されています。置けないキーはSecretにし、ブラウザ公開用の設定にならない名前にします。また、変更は過去のデプロイには反映されず、新しいデプロイにだけ適用されます。キーを入れ替えたら再デプロイが必要です。
ただし、サーバー側の処理を誰でも呼べる状態にすると、そこが新しい穴になります。呼び出せる相手の制限や回数制限も、移したあとに必ず確認してください。
流出に気づいたときの動き
順番が大切です。履歴の書き換えよりも、キーを無効にするほうが先です。
- キーの種類を確認する。 公開してよい種類で制限済みなら、制限の見直しで足ります。Googleの指針も、すでに制限されたキーの漏えいでは制限の追加を優先し、再生成は最後の手段としています。
- 置けない種類なら、すぐ無効化または再発行する。 GitHubの機密データ削除の案内でも、まず秘密情報を無効化・変更することが重要で、それだけで足りる場合もあるとされています。
- 使われた形跡を確認する。 提供元の管理画面で、身に覚えのない利用や請求がないかを見ます。キーを入れ替える前に利用状況を確認し、段階的に移す計画を立てるようGoogleは勧めています。
- 新しいキーをサーバー側の環境変数に入れて再デプロイする。
- 必要なら履歴を書き換える。 git-filter-repo などでGitの履歴から消せますが、コミットのハッシュが変わり、共同作業者の古いコピーから再び混入するおそれがあります。GitHubの案内では、キャッシュされた表示の削除にGitHubサポートへの連絡が必要です。無効化済みのキーなら、この作業は必須ではありません。
GitHubの検出機能に頼りすぎない
GitHubのシークレットスキャンは、公開リポジトリでは無料で自動的に動きます。ただし、提携先サービスの認証情報は提供元に直接通知される仕組みで、リポジトリ側のアラートには出ません。通知が来ないことは安全の証明になりません。上の検索は、公開前に自分で行ってください。
公開前の最終チェック
- ビルド成果物で、上の語句を検索した(ヒットは全件判定した)
- 置けないキーはSecretとしてサーバー側にあり、ブラウザ公開用の設定になっていない
- 置いてよいキーには、API制限とドメイン制限(Firebaseなら加えてSecurity RulesとApp Check)がある
- .env 系のファイルが .gitignore に入っており、履歴にも残っていない
- 過去に貼ったキーは、無効化してから新しいものに替えた

