結論:混乱の正体は「名前違い」
ポイント:今回のつまずきは、Firebase Storage の「バケットの実名」と「アクセス用ドメイン」を取り違えたことが原因でした。通常はデフォルトバケットが <プロジェクトID>.appspot.com
ですが、私の環境ではなぜか <プロジェクトID>.firebasestorage.app
が表示されており、ここで思い込みが生まれました。
理由:Admin SDK や Google Cloud Storage(JSON API) は“バケットの実名”を参照します。一方、Web/モバイルのクライアントSDKは firebasestorage.app
ドメインを入り口として扱います。見た目が似ているため、どちらを設定すべきか迷いやすい構造になっていました。
例:システムの実装は「デフォルト=<プロジェクトID>.appspot.com
」を前提にしていましたが、現実のバケットは <プロジェクトID>.firebasestorage.app
でした。そのため Functions から画像を読みにいくと 404 が返り、内部エラーに連鎖しました。
- Admin SDK:実名のバケットを使う
- クライアントSDK:
firebasestorage.app
のドメイン経由 - コンソールの「gs://…」に出る値が正解
結論:UIに表示される gs://
の値(実名)を信じ、環境変数と初期化コードで明示的に指定すると迷いが消えます。
先に起きた症状とエラー
404:バケットが存在しないと表示された
ポイント:JSON API で https://storage.googleapis.com/storage/v1/b/<プロジェクトID>.appspot.com/o/…
にアクセスしたところ、The specified bucket does not exist. が返ってきました。
理由:リクエストが参照していたのは「想定上のデフォルト名」であり、実環境のバケット名と一致していなかったためです。実態は <プロジェクトID>.firebasestorage.app
で、コードと現物がずれていました。
- 表示エラー:
404 not found
- 原因:バケット名の取り違え
- 確認:Firebase Console の Storage で gs://~ をチェック
まとめ:404 は「権限」ではなく「名前違い」のサインです。最初にバケットの実名を見直すと早道となります。
401:匿名ユーザーに閲覧権限がないと言われた
ポイント:別経路(ブラウザ)でオブジェクト URL をそのまま叩くと、Anonymous caller does not have storage.objects.get access… と表示されました。
理由:認証なしのアクセスで、セキュリティルールに合致しなかったためです。たとえバケット名が合っていても、ルールや署名付きURLの準備がなければ取得できません。
- 表示エラー:
401 Unauthorized
- 対処:Firebase Auth を通すか、署名付きURL/公開設定を利用
- 運用:基本は
getDownloadURL()
を使う
まとめ:401 は「認証・公開」の不足が原因です。名前を直した後に発生することもあるため、順番に切り分けましょう。
500:Functions 側で内部エラーが起きた
ポイント:フロントから Callable Functions を呼ぶと 500 系の内部エラーが出る場面がありました。
理由:内部で Admin SDK が誤ったバケット名を参照し、存在しないバケットに対してオブジェクト取得を試みたためです。一次原因は「名前違い」、二次的に Functions が落ちて見える形でした。
- 症状:Functions ログにストレージ 404 → 例外
- 原因:Admin SDK の
storageBucket
が未設定/誤設定 - 対処:環境変数と初期化コードで明示
まとめ:Functions の 500 は、上流のストレージ設定ミスが飛び火した結果でした。ログでバケット名を出すと早く収束します。
2つの名の違いを理解
<ID>.appspot.com は GCS の実名(Admin 用)
ポイント:<プロジェクトID>.appspot.com
は Google Cloud Storage における「バケットの正式名称」です。Admin SDK や JSON API はこの値を使って操作します。
理由:サーバー側の処理は IAM 権限で GCS を直接呼び出すため、URLドメインではなくバケットの実名を必要とします。ここを取り違えると 404 に直結します。
- 使いどころ:Cloud Functions/バックエンド処理
- 確認方法:Storage ルートの gs://… 表示が唯一の正解
- 設定先:Admin SDK 初期化の
storageBucket
まとめ:サーバーは“実名”で動きます。まずは Console の gs://
を写し取ることが大切です。
<ID>.firebasestorage.app は SDK の入口(クライアント用)
ポイント:<プロジェクトID>.firebasestorage.app
はクライアントSDKのアクセス用ドメインです。Auth やセキュリティルールを適用しながら、ファイル配信の入り口として機能します。
理由:Web/モバイルでは認証・ルールを簡単に扱う必要があるため、SDK がこのドメインを抽象化してくれます。実名バケットを手組みで叩く必要はありません。
- 使いどころ:ブラウザ/アプリのダウンロード・アップロード
- 取得方法:
getDownloadURL(ref)
を利用 - 混同注意:これは「バケット名」ではなく「アクセスの窓口」
まとめ:フロントはドメイン、サーバーは実名。役割を分けて考えると理解が進みます。
JSON API とダウンロード URL の違い
ポイント:GCS の JSON API は storage.googleapis.com
でバケットの実名を扱います。対して、Firebase のダウンロード URL は SDK がよしなに生成し、認証やルールを通した取得を助けます。
- JSON API:
https://storage.googleapis.com/storage/v1/b/<バケット名>/o/…
- SDK URL:
https://firebasestorage.googleapis.com/v0/b/<バケット名>/o/…
- 原則:フロントは SDK、サーバーは Admin SDK
まとめ:URLの形が違うだけでなく、想定権限やルールも変わります。用途に合った入り口を選ぶと失敗しにくくなります。
何を確認すべきか
まず「実在するバケット名」を特定する
ポイント:Firebase Console の Storage ルートに表示される gs://…
が現物のバケット名です。ここが <プロジェクトID>.appspot.com
とは限りません。
- 開発・本番で値が異なるケースに注意
- コピー&ペーストで転記して誤記を防止
- ドキュメントにも明記しておくと安全
まとめ:「UIに出ている値こそ正解」です。推測せず、まず見る。これだけで多くの不具合が未然に防げます。
Functions(Admin SDK)の参照先をログで確認
ポイント:Admin SDK 初期化時に storageBucket
と「決定元」をログ出力すると、ミスにすぐ気づけます。
- 起動ログ例:storageBucket=…, source=env / FIREBASE_CONFIG / fallback
- CI/CD でも出力して差分検知
- 問題発生時の一次切り分けが圧倒的に速くなる
まとめ:可視化が最速のデバッグです。値と決定ロジックを出せば、誰でも追える状態になります。
クライアント設定の一致を確認
ポイント:Web 側の firebaseConfig.storageBucket
にも、実名のバケットを設定します。SDK が内部で利用するため、ここの不一致は画像取得の失敗につながります。
- 原則:フロントも同じ実名を設定
- 取得は
getDownloadURL()
を必ず使用 - 直叩きURLの手組みは避ける
まとめ:バックとフロントで同じ名を使うと、環境差異が減ります。設定の単純化が再発防止に効いてきます。
解決手順(最短ルート)
ポイント:環境変数と初期化コードで、使うバケットを明示的に固定しました。通常は <プロジェクトID>.appspot.com
がデフォルトですが、今回は <プロジェクトID>.firebasestorage.app
が実体だったため、システムの前提と噛み合っていませんでした。
実施内容:Functions 直下に functions/.env
を作成して、次を設定しました。
STORAGE_BUCKET=<プロジェクトID>.firebasestorage.app
さらに、Admin SDK 初期化コードでもバケットを明示し、どの経路でも最終的にこの値が使われるように強制しています。
// functions/src/firebase.ts(例)
import { initializeApp } from 'firebase-admin/app';
const env = process.env;
const cfg = env.FIREBASE_CONFIG ? JSON.parse(env.FIREBASE_CONFIG) : {};
const STORAGE_BUCKET =
env.STORAGE_BUCKET
?? cfg.storageBucket
?? '<プロジェクトID>.firebasestorage.app'; // Consoleの gs:// を写す
initializeApp({ storageBucket: STORAGE_BUCKET });
// 起動ログ(決定元も出すと安心)
console.log('[bootstrap] admin initialized', {
storageBucket: STORAGE_BUCKET,
source: env.STORAGE_BUCKET ? 'env.STORAGE_BUCKET'
: cfg.storageBucket ? 'FIREBASE_CONFIG.storageBucket'
: 'fallback'
});
- 環境変数により値を固定化
- 初期化コードで最終決定
- ログ出力で検証しやすくする
結果:Functions の画像取得が安定し、404/500 の再発が止まりました。前提のズレを設定で吸収できた形です。
エラー別チェック表
404 のとき(まず名前を疑う)
- Console の gs://… とコードの指定が一致しているか
- 環境差(dev/prod)で名が違っていないか
- スペル・大小文字のミスがないか
まとめ:404 は名前違いの警告灯です。権限より先に、名寄せを行うと早く片づきます。
401 のとき(認証・公開設定を点検)
- ユーザーはログイン済みか
- ダウンロードは
getDownloadURL()
を使用しているか - 公開配布なら署名付きURLや公開設定を検討
まとめ:401 は未認証アクセスのサインです。Auth とルールを見直すと改善します。
403 のとき(Functions の権限不足)
- Functions のサービスアカウントに Storage Object Viewer 以上が付与されているか
- 組織ポリシーでブロックされていないか
- バケットのリージョンと関数のリージョンが整合しているか
まとめ:403 はIAM の話です。だれが何をするか、の許可を丁寧に確認します。
再発防止のベストプラクティス
環境変数と起動ログを標準化する
STORAGE_BUCKET
を環境変数で必ず定義- 初期化時に値と決定元をログ出力
- ドキュメント(README/運用メモ)に手順を残す
まとめ:「見える化」と「単一の定義場所」が再発を防ぎます。チーム全員が同じ手順で検証できる状態を維持しましょう。
命名・リージョン・環境差のルールを決める
- 開発・本番で命名規則を統一(例:
<id>-dev
/<id>-prod
) - リージョンを揃えて遅延と権限の事故を回避
- 環境ごとに
.env
を用意して切り替えを明確化
まとめ:最初にルールを決めると、後からのブレが起きません。設定の一貫性が品質を支えます。
まとめ
ポイント:今回つまづいた本質は「名前違い」でした。通常は <プロジェクトID>.appspot.com
がデフォルトと考えがちですが、実際の環境では <プロジェクトID>.firebasestorage.app
が現物だったため、システムの前提と食い違っていました。

- まず Console の gs://… を確認する
functions/.env
にSTORAGE_BUCKET=<プロジェクトID>.firebasestorage.app
を設定- Admin SDK 初期化で
storageBucket
を明示し、ログで検証 - クライアントは
getDownloadURL()
を使い、直叩きを避ける
結び:設定を明示して可視化すると、迷いが消えます。これだけで「デフォルトバケットの落とし穴」を安全に迂回できるはずです。
コメント