GEMINI LABEN
NANOLITE — Nano Banana 2 Liteが登場しました。Googleで最も速く、最もコスト効率の高いGemini Imageモデルで、軽量な画像生成を安く回したい用途に向いていますOMNIFLASH — Gemini Omni Flashがpublic previewになりました。ネイティブにマルチモーダルなモデルで、企業や開発者が独自の動的な動画ワークフローを構築できますAGENTS — Managed Agentsが拡張されました。background: trueでサーバー側の非同期実行とポーリング、リモートMCPサーバー連携、対話をまたぐ認証情報のリフレッシュに対応しますMEMORY — Memory BankのIngestEvents APIが一般提供になりました。イベントの取り込みとメモリ生成を分離し、コンテンツを継続的にストリームできますTHROUGHPUT — Provisioned Throughputで、同一モデル・同一リージョンに対して最大7件の保留オーダーを提出できるようになりましたDEPRECATE — 画像生成モデルは8月17日に、Gemini Enterprise Agent PlatformのGrok 4.1系は8月20日に停止される予定ですNANOLITE — Nano Banana 2 Liteが登場しました。Googleで最も速く、最もコスト効率の高いGemini Imageモデルで、軽量な画像生成を安く回したい用途に向いていますOMNIFLASH — Gemini Omni Flashがpublic previewになりました。ネイティブにマルチモーダルなモデルで、企業や開発者が独自の動的な動画ワークフローを構築できますAGENTS — Managed Agentsが拡張されました。background: trueでサーバー側の非同期実行とポーリング、リモートMCPサーバー連携、対話をまたぐ認証情報のリフレッシュに対応しますMEMORY — Memory BankのIngestEvents APIが一般提供になりました。イベントの取り込みとメモリ生成を分離し、コンテンツを継続的にストリームできますTHROUGHPUT — Provisioned Throughputで、同一モデル・同一リージョンに対して最大7件の保留オーダーを提出できるようになりましたDEPRECATE — 画像生成モデルは8月17日に、Gemini Enterprise Agent PlatformのGrok 4.1系は8月20日に停止される予定です
記事一覧/API / SDK
API / SDK/2026-05-29中級

Gemini API キーに HTTP リファラー制限をかけたら本番サイトから 403 エラーになる原因と対処

Google Cloud Console で API キーに HTTP リファラー制限を設定したら、ブラウザから叩く本番サイトでも 403 エラーが返るようになった、というつまずきの原因と対処を、リファラー文字列の書式・SDK 別の挙動・サーバー側プロキシへの逃し方、さらにブラウザ直叩きで起きる CORS エラーとの切り分けまで含めて整理します。

gemini-api279troubleshooting57api-key3security8referrergoogle-cloud6

「ブラウザから叩くデモページ用に Gemini API キーを発行して、流出が怖いから HTTP リファラー制限をかけた。動作確認は通ったのに、本番ドメインに乗せた瞬間に 403 PERMISSION_DENIED で全リクエストが落ちる」— 個人開発で API キーを Web アプリに同梱しようとしたとき、最初に踏みやすい地雷です。

私自身、自社サイト(dolice.design)でちょっとした AI ガジェットを公開しようとしたときに同じ症状に行き当たり、リファラー文字列の書式と、SDK が裏で叩いている URL の関係を把握していなかったせいで丸半日溶かしました。個人開発で API キーを Web アプリに同梱する構成は何度か組んできましたが、API キー制限まわりは「壊れたときに焦らないこと」が一番大切だと感じています。ここでは原因の切り分けと、本番で安全に運用するための逃し方を整理します。

まず確認すべき症状の特徴

リファラー制限が原因の 403 は、以下のような症状の組み合わせで出ます。

  • ローカル開発(localhost / 127.0.0.1)では成功するのに、本番ドメインからだけ落ちる、もしくはその逆
  • ブラウザの DevTools で見ると OriginReferer が両方付いている GET/POST なのに 403
  • レスポンス本文に API keys with referer restrictions cannot be used with this APIRequests from referer ... are blocked というメッセージが含まれる
  • サーバーサイドからの呼び出し(Node.js のバックエンド、Cloud Functions、Cron)だけが落ちる

逆に「キーそのものが無効」「課金が無効」のときは 400 INVALID_ARGUMENT403 SERVICE_DISABLED といった別メッセージになるため、本文を読めば切り分けはほぼ確定します。

なぜ「設定したはずなのに通らない」のか — 4 つの典型原因

1. スキームを書かずに example.com/* と入れている

Google Cloud Console の HTTP リファラー欄は、内部的に「URL を組み立ててから前方一致でマッチング」しています。example.com/* だけだとブラウザが送ってくる https://example.com/pathhttps:// 部分でマッチしません。正しくは https://example.com/* のように スキームを必ず付ける こと。https://*.example.com/* のようにサブドメインまで含めたい場合も、必ず先頭にスキームが必要です。

2. パス末尾の /* を省略している

https://example.com のように末尾を省略すると、https://example.com/ 1 個しかマッチしません。/api/gemini のような任意パスからも叩きたいなら https://example.com/* と書きます。私はここで「サブパスからのリクエストだけ落ちる」現象に遭遇して、ルーティングが壊れたのかと丸 1 時間勘違いしました。

3. SDK の裏で呼ばれる URL に対して付けていない

Vertex AI モードで動かしている場合、リクエスト先は aiplatform.googleapis.com です。素の Gemini API は generativelanguage.googleapis.com です。リファラー制限はクライアントが送る Referer ヘッダに対するチェックなので、エンドポイントの違いではなく どの呼び出しがブラウザから直接行われているか を確認するのが正解です。Edge Functions やプロキシ経由で叩いている場合、ブラウザからの Referer はプロキシで落ちるため意味がなくなります。

4. サーバーサイド呼び出しに付けてしまっている

ここが一番焦りやすい点です。サーバー(Node.js / Cloud Run / Cloud Functions)からのリクエストには Referer ヘッダが原則として付きません。リファラー制限を付けると サーバーからの呼び出しが常に 403 になります。サーバー用と Web 用でキーを分けるのが正解です。

切り分け 30 秒コマンド

curl で再現できるかどうかが最短ルートです。

# 1. リファラーなし(= サーバーサイド相当)
curl -s -o /dev/null -w "%{http_code}\n" \
  "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=YOUR_GEMINI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"contents":[{"parts":[{"text":"hi"}]}]}'
 
# 2. 許可ドメインのリファラーを偽装
curl -s -o /dev/null -w "%{http_code}\n" \
  "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=YOUR_GEMINI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Referer: https://example.com/app" \
  -d '{"contents":[{"parts":[{"text":"hi"}]}]}'

両方 403 なら、許可リストの書式が間違っている可能性が高い。1 が 403 で 2 が 200 なら、サーバーサイド呼び出しが落ちているだけなので、後述の「逃し方」に進みます。

別の壁:ブラウザから直接叩くと CORS で弾かれる

リファラー制限を正しく直したのに、ブラウザからの呼び出しだけ動かない。しかもレスポンス本文に 403 すら出ず、コンソールに blocked by CORS policy と表示される。これは API キーの問題ではありません。generativelanguage.googleapis.com は、ブラウザからのクロスオリジン直接呼び出しを想定しておらず、Access-Control-Allow-Origin ヘッダを返さないためです。

切り分けは DevTools の Network タブが早いです。

  • 当該リクエストが (failed) または CORS error になり、Response が空 → CORS で止まっています。サーバーには届いていません。
  • ステータス 403 のボディが返っている → こちらはリファラー/キー制限の話なので、前の節に戻ります。

つまり「リファラーを直す」だけでは一生解決しません。preflight の OPTIONS がそもそも通らないので、fetch のオプションをいくらいじっても無駄です。私自身、mode: "cors"headers を変えながら半日溶かしたことがありますが、ブラウザ側でできることは何もありませんでした。

解決策は次節の「ブラウザから直接叩かない」とまったく同じで、自前プロキシを一枚挟みます。違いは、プロキシ側で CORS ヘッダを明示的に返し、preflight の OPTIONS に応答してあげる点です。

// Cloudflare Workers: CORS 対応プロキシ
const ALLOW_ORIGIN = "https://example.com"; // ワイルドカードにしない
 
export default {
  async fetch(req: Request, env: Env) {
    // preflight への応答
    if (req.method === "OPTIONS") {
      return new Response(null, { status: 204, headers: corsHeaders() });
    }
    if (req.method !== "POST") {
      return new Response("Method Not Allowed", { status: 405, headers: corsHeaders() });
    }
    const upstream = await fetch(
      "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=" + env.GEMINI_API_KEY_SERVER,
      { method: "POST", headers: { "content-type": "application/json" }, body: await req.text() }
    );
    return new Response(upstream.body, {
      status: upstream.status,
      headers: { ...corsHeaders(), "content-type": "application/json" },
    });
  },
};
 
function corsHeaders() {
  return {
    "Access-Control-Allow-Origin": ALLOW_ORIGIN,
    "Access-Control-Allow-Methods": "POST, OPTIONS",
    "Access-Control-Allow-Headers": "content-type",
  };
}

ここで Access-Control-Allow-Origin* にすると、確かに CORS は通りますが、今度はそのプロキシが誰からでも叩ける踏み台になります。キーは隠せても、他人にあなたのクォータを使われては本末転倒です。許可するオリジンは本番ドメインだけに絞り、プロキシ側でレート制限も入れておきます。

本番で安全に運用するための逃し方

A. キーを 2 系統に分ける(推奨)

最も素直な解決策です。Web 用キーは HTTP リファラー制限を厳格に付け、サーバー用キーは IP アドレス制限か API 制限のみを付けます。pricing.ts.env で名前を分けておくと事故が起きにくいです。

// Web 用(埋め込み・難読化はするが流出前提のキー)
const WEB_GEMINI_API_KEY = process.env.NEXT_PUBLIC_GEMINI_API_KEY_WEB;
 
// サーバー用(流出絶対 NG・IP 制限あり)
const SERVER_GEMINI_API_KEY = process.env.GEMINI_API_KEY_SERVER;

B. ブラウザから直接叩かない(より推奨)

長期的には、API キーをブラウザに同梱せず、自前の薄いプロキシを経由する形に寄せるのが安全です。Cloudflare Workers や Cloud Run で 10 行程度のフォワーダーを挟むだけで、キーをサーバー側に閉じ込められます。

// Cloudflare Workers: gemini-proxy
export default {
  async fetch(req: Request, env: Env) {
    if (req.method !== "POST") return new Response("Method Not Allowed", { status: 405 });
    const body = await req.text();
    const upstream = await fetch(
      "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=" + env.GEMINI_API_KEY_SERVER,
      { method: "POST", headers: { "content-type": "application/json" }, body }
    );
    return new Response(upstream.body, { status: upstream.status, headers: { "content-type": "application/json" } });
  }
};

この形にすると、HTTP リファラー制限ではなく 自前プロキシ側で Origin チェック + レート制限 を入れるほうが堅牢になります。個人開発でアプリやサイトを長く運用してきた経験から言うと、流出した API キーで一晩のうちに数万円分のクォータを消費されるリスクは現実にあり、キーをブラウザに直接見せる構成は最終的にどこかで卒業することになります。

C. App Check / Firebase AI Logic を併用する

Web/iOS/Android からの呼び出しに対しては、Firebase AI Logic(旧 Vertex AI for Firebase)の App Check 連携を組み合わせると、キーそのものを露出させずに「正規アプリからの呼び出しだけ通す」状態にできます。リファラー制限よりも強い保護になります。

設定後のチェックリスト

  • 許可リストの全エントリにスキーム(https://)が入っている
  • パス末尾に /* が付いている
  • 開発用に http://localhost:* http://127.0.0.1:* も追加してある(必要な場合)
  • サーバーサイド呼び出しは別キーになっている
  • 反映までは数分かかるため、変更直後の 403 で焦らない(最大 5 分程度)

実装の参考になれば幸いです。最後までお読みいただきありがとうございました。

シェア

お読みいただきありがとうございます

Gemini Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

API / SDK2026-04-28
Gemini APIで GEMINI_API_KEY と GOOGLE_API_KEY のどちらを使うか問題を解決する
Gemini API で GEMINI_API_KEY と GOOGLE_API_KEY のどちらを使うべきか迷ったときに読む実践ガイド。SDK ごとの優先順位、Vertex AI モードの自動切替、つまずきやすいハマりパターンを切り分け手順とコード例で整理します。
API / SDK2026-03-26
Gemini API 本番環境セキュリティ — APIキー管理・プロンプトインジェクション対策・監査ログの実装
Gemini APIを本番環境で安全に運用するためのセキュリティ実装パターンを解説。APIキーのローテーション、入出力サニタイズ、プロンプトインジェクション防御、監査ログ、レート制限を網羅する上級ガイド。
API / SDK2026-07-01
AI Studio で通ったプロンプトが API では静かに崩れるとき — 差分を計測して切り分ける運用メモ
AI Studio では完璧に動いたプロンプトが、自分のコードから Gemini API を呼ぶと空応答や 404 になる。目視ではなく、設定差と finish_reason・使用トークン・解決モデル名を機械的に記録する小さなハーネスで、原因別に切り分ける運用手順をまとめました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →