
【アップデート】Cloud CDN と外部 Application Load Balancer でプライベートな Cloud Storage バケットにアクセスできるようになりました
はじめに
こんにちは。
クラウド事業本部コンサルティング部の渡邉です。
2026年7月3日、Cloud CDN および外部 Application Load Balancer において、プライベートな Cloud Storage バケットへのアクセス(Private Bucket Access)が一般提供(GA)になりました。
これまで Cloud CDN のバックエンドバケットとして Cloud Storage を利用する場合、バケットをパブリックに公開するか、署名付き URL を使う必要がありました。
バケットをパブリックに公開することに抵抗がある方も多かったのではないでしょうか。
今回のアップデートにより、バケットを公開せずに IAM 経由のサービスアカウント認証でコンテンツを配信できるようになり、よりセキュアな静的コンテンツ配信が実現します。
本記事では、Private Bucket Access の仕組みと設定手順をご紹介します。
Private Bucket Access の仕組み
従来の構成との比較
従来、Cloud CDN のバックエンドバケットとして Cloud Storage を利用する場合の方式として、バケットをパブリックに公開するか署名付きURLを利用する必要がありました。
バケットをパブリックに公開する場合、設定としては簡単ですが、パブリックに公開しているため、誰でもバケットにアクセスすることができてしまうデメリットがありました。
署名付き URLを利用すればバケットをプライベートにすることは可能ですが、独自の署名付きURLを生成する仕組みが必要になります。
今回のPrivate Bucket Accessは、Load Balancer専用のサービスアカウントからのみアクセス可能とするIAMを付与することで、バケットをプライベートにしながらコンテンツを配信することができます。
| 方式 | セキュリティ | 手間 |
|---|---|---|
| バケットをパブリック公開 | 誰でもアクセス可能 | 設定が簡単 |
| 署名付き URL | URL ごとに有効期限あり | URL 生成の仕組みが必要 |
| Private Bucket Access(今回) | LB 専用 SA のみアクセス可 | IAM 設定のみ |
サービスアカウントについて
Private Bucket Access では、Google が管理する以下の形式のサービスアカウントを使用します。
service-PROJECT_NUM@https-lb.iam.gserviceaccount.com
このサービスアカウントに roles/storage.objectViewer ロールを付与することで、Cloud CDN および外部 Application Load Balancer がプライベートバケットのオブジェクトを読み取れるようになります。
サポート対象のロードバランサー
| ロードバランサー | Private Bucket Access |
|---|---|
| Global external Application Load Balancer | サポート |
| Classic Application Load Balancer | サポート |
| Regional external Application Load Balancer | 非サポート |
| Regional internal Application Load Balancer | 非サポート |
実際に試してみる
前提条件
- Google Cloud プロジェクトで課金が有効になっていること
gcloudCLI がインストール・認証済みであること- プロジェクトで以下の API が有効になっていること
- Cloud Storage API
- Compute Engine API
事前準備: 変数を設定する
以降のコマンドで共通して使用する変数を設定します。YOUR_PROJECT_ID を実際のプロジェクト ID に置き換えてください。
PROJECT_NUM はサービスアカウントのメールアドレス(service-PROJECT_NUM@https-lb.iam.gserviceaccount.com)に使用します。
export PROJECT_ID="YOUR_PROJECT_ID"
export PROJECT_NUM=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
export BUCKET_NAME="private-cdn-bucket" # グローバルに一意なバケット名
export BACKEND_BUCKET_NAME="cdn-backend-bucket"
export LB_IP_NAME="cdn-lb-ip"
export URL_MAP_NAME="cdn-url-map"
export PROXY_NAME="cdn-http-proxy"
export FORWARDING_RULE_NAME="cdn-forwarding-rule"
ステップ1: プライベートな Cloud Storage バケットを作成する
Uniform Bucket-Level Access(統一バケットレベルアクセス)を有効にしたプライベートバケットを作成します。この設定により ACL が無効化され、IAM のみでアクセス制御が行われます。
gcloud storage buckets create gs://$BUCKET_NAME \
--project=$PROJECT_ID \
--default-storage-class=standard \
--location=asia-northeast1 \
--uniform-bucket-level-access
Creating gs://private-cdn-bucket/...
バケットがプライベートであること(allUsers に権限がないこと)を確認します。
gcloud storage buckets get-iam-policy gs://$BUCKET_NAME
bindings:
- members:
- projectEditor:YOUR_PROJECT_ID
- projectOwner:YOUR_PROJECT_ID
role: roles/storage.legacyBucketOwner
- members:
- projectViewer:YOUR_PROJECT_ID
role: roles/storage.legacyBucketReader
- members:
- projectEditor:YOUR_PROJECT_ID
- projectOwner:YOUR_PROJECT_ID
role: roles/storage.legacyObjectOwner
- members:
- projectViewer:YOUR_PROJECT_ID
role: roles/storage.legacyObjectReader
etag: CAE=
ステップ2: テスト用オブジェクトをアップロードする
動作確認に使用するファイルをバケットにアップロードします。
echo "Hello from Cloud CDN Private Bucket!" > test.txt
gcloud storage cp test.txt gs://$BUCKET_NAME/test.txt
Copying file://test.txt to gs://private-cdn-bucket/test.txt
Completed files 1/1 | 37.0B/37.0B

Cloud Storage バケットへのファイルアップロード確認
ステップ3: 静的 IP アドレスを予約する
ロードバランサーのフロントエンドに使用するグローバル静的 IP アドレスを予約します。
gcloud compute addresses create $LB_IP_NAME \
--network-tier=PREMIUM \
--ip-version=IPV4 \
--global
予約された IP アドレスを確認します。
gcloud compute addresses describe $LB_IP_NAME \
--format="get(address)" \
--global
YOUR_LB_IP

予約された IP アドレス
ステップ4: バックエンドバケットを作成する(Cloud CDN 有効)
Cloud CDN を有効にして、プライベートバケットと紐づけたバックエンドバケットを作成します。
gcloud compute backend-buckets create $BACKEND_BUCKET_NAME \
--gcs-bucket-name=$BUCKET_NAME \
--enable-cdn \
--cache-mode=FORCE_CACHE_ALL
Created [https://www.googleapis.com/compute/v1/projects/YOUR_PROJECT_ID/global/backendBuckets/cdn-backend-bucket].
NAME: cdn-backend-bucket
GCS_BUCKET_NAME: private-cdn-bucket
ENABLE_CDN: True
キャッシュの TTL を設定します(ここでは 24 時間)。FORCE_CACHE_ALL モードでは --max-ttl は使用できず、--default-ttl を使います。
gcloud compute backend-buckets update $BACKEND_BUCKET_NAME \
--default-ttl=86400
Updated [https://www.googleapis.com/compute/v1/projects/YOUR_PROJECT_ID/global/backendBuckets/cdn-backend-bucket].
ステップ5: ロードバランサーを構成する
URL マップ、ターゲットプロキシ、フォワーディングルールを順に作成します。
URL マップの作成
gcloud compute url-maps create $URL_MAP_NAME \
--default-backend-bucket=$BACKEND_BUCKET_NAME
Created [https://www.googleapis.com/compute/v1/projects/YOUR_PROJECT_ID/global/urlMaps/cdn-url-map].
NAME: cdn-url-map
DEFAULT_SERVICE: backendBuckets/cdn-backend-bucket
ターゲット HTTP プロキシの作成
gcloud compute target-http-proxies create $PROXY_NAME \
--url-map=$URL_MAP_NAME
Created [https://www.googleapis.com/compute/v1/projects/YOUR_PROJECT_ID/global/targetHttpProxies/cdn-http-proxy].
NAME: cdn-http-proxy
URL_MAP: cdn-url-map
フォワーディングルールの作成
gcloud compute forwarding-rules create $FORWARDING_RULE_NAME \
--load-balancing-scheme=EXTERNAL_MANAGED \
--network-tier=PREMIUM \
--address=$LB_IP_NAME \
--global \
--target-http-proxy=$PROXY_NAME \
--ports=80
Created [https://www.googleapis.com/compute/v1/projects/YOUR_PROJECT_ID/global/forwardingRules/cdn-forwarding-rule]

作成したロードバランサー
ステップ6: バケットに IAM 権限を付与する
LB サービスアカウントにバケットへの読み取り権限を付与します。
gcloud storage buckets add-iam-policy-binding gs://$BUCKET_NAME \
--member="serviceAccount:service-${PROJECT_NUM}@https-lb.iam.gserviceaccount.com" \
--role=roles/storage.objectViewer
bindings:
- members:
- projectEditor:YOUR_PROJECT_ID
- projectOwner:YOUR_PROJECT_ID
role: roles/storage.legacyBucketOwner
- members:
- projectViewer:YOUR_PROJECT_ID
role: roles/storage.legacyBucketReader
- members:
- projectEditor:YOUR_PROJECT_ID
- projectOwner:YOUR_PROJECT_ID
role: roles/storage.legacyObjectOwner
- members:
- projectViewer:YOUR_PROJECT_ID
role: roles/storage.legacyObjectReader
- members:
- serviceAccount:service-YOUR_PROJECT_NUMBER@https-lb.iam.gserviceaccount.com
role: roles/storage.objectViewer
etag: CAI=
kind: storage#policy
resourceId: projects/_/buckets/private-cdn-bucket
version: 1
ステップ7: 設定を確認する
バックエンドバケットの Cloud CDN 設定と IAM バインディングを確認します。
# バックエンドバケットの Cloud CDN 有効化を確認
gcloud compute backend-buckets describe $BACKEND_BUCKET_NAME \
--format="yaml(name,bucketName,enableCdn,cdnPolicy)"
bucketName: private-cdn-bucket
cdnPolicy:
cacheMode: FORCE_CACHE_ALL
defaultTtl: 86400
negativeCaching: true
requestCoalescing: true
serveWhileStale: 86400
enableCdn: true
name: cdn-backend-bucket
enableCdn: true が返れば Cloud CDN が有効です。
# バケットの IAM ポリシーを確認
gcloud storage buckets get-iam-policy gs://$BUCKET_NAME
bindings:
- members:
- projectEditor:YOUR_PROJECT_ID
- projectOwner:YOUR_PROJECT_ID
role: roles/storage.legacyBucketOwner
- members:
- projectViewer:YOUR_PROJECT_ID
role: roles/storage.legacyBucketReader
- members:
- projectEditor:YOUR_PROJECT_ID
- projectOwner:YOUR_PROJECT_ID
role: roles/storage.legacyObjectOwner
- members:
- projectViewer:YOUR_PROJECT_ID
role: roles/storage.legacyObjectReader
- members:
- serviceAccount:service-YOUR_PROJECT_NUMBER@https-lb.iam.gserviceaccount.com
role: roles/storage.objectViewer
etag: CAI=
出力の bindings 内に roles/storage.objectViewer で service-${PROJECT_NUM}@https-lb.iam.gserviceaccount.com が含まれていれば設定完了です。
動作確認
設定が完了したら、以下の3点を確認します。
1. バケットへの直接アクセスが拒否されることを確認
プライベートバケットとして正しく設定されていれば、Cloud Storage への直接アクセスは 403 Forbidden になります。
curl -o /dev/null -w "%{http_code}" \
https://storage.googleapis.com/$BUCKET_NAME/test.txt
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 673 100 673 0 0 6035 0 --:--:-- --:--:-- --:--:-- 6063
403
403 が返れば、バケットが外部から保護されています。
2. LB 経由でコンテンツにアクセスできることを確認
# ロードバランサーの IP アドレスを取得
LB_IP=$(gcloud compute addresses describe $LB_IP_NAME \
--format="get(address)" \
--global)
# ヘッダーを表示しながらアクセス確認
curl -D- -o /dev/null http://$LB_IP/test.txt
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0HTTP/1.1 200 OK
x-guploader-uploadid: AJ5rDhEQxkda4hkzVbDGYXP_oVYFqrSJ01zo4FHHKRKd-X1ioovc4B4rd5GcqjvpSOdbKLlYBwNDhj8
x-goog-generation: 1782686040174590
x-goog-metageneration: 1
x-goog-stored-content-encoding: identity
x-goog-stored-content-length: 37
x-goog-hash: crc32c=b2a0wA==
x-goog-hash: md5=VP+xVbgH7gFLUsy3jToZaA==
x-goog-storage-class: STANDARD
accept-ranges: bytes
Content-Length: 37
server: UploadServer
via: 1.1 google
Date: Sun, 05 Jul 2026 20:50:34 GMT
Cache-Control: public,max-age=86400
Last-Modified: Sun, 28 Jun 2026 22:34:00 GMT
ETag: "54ffb155b807ee014b52ccb78d3a1968"
Content-Type: text/plain
Age: 211
100 37 100 37 0 0 2062 0 --:--:-- --:--:-- --:--:-- 2176
初回アクセス(キャッシュミス)では Age: 0、2回目以降のアクセス(キャッシュヒット)では Age: N(N は秒数)が返えるようになります。
コンテンツも無事取得できていました。
curl http://$LB_IP/test.txt
Hello from Cloud CDN Private Bucket!
3. Cloud Logging でキャッシュ動作を確認
Logs Explorer で以下のクエリを実行すると、キャッシュヒットのログを確認できます。
resource.type="http_load_balancer"
jsonPayload.statusDetails="response_from_cache"
キャッシュヒット時のログには次のフィールドが含まれます。
{
"insertId": "yqd7ldf4ahsgs",
"jsonPayload": {
"cacheId": "NRT",
"statusDetails": "response_from_cache",
"backendTargetProjectNumber": "projects/YOUR_PROJECT_NUMBER",
"cachePolicySource": "CACHE_POLICY_SOURCE_BACKEND",
"@type": "type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry",
"remoteIp": "YOUR_CLIENT_IP",
"cacheDecision": [
"RESPONSE_HAS_CACHE_CONTROL",
"RESPONSE_CACHE_CONTROL_NO_CACHE",
"RESPONSE_HAS_ETAG",
"RESPONSE_HAS_LAST_MODIFIED",
"RESPONSE_HAS_EXPIRES",
"RESPONSE_HAS_CONTENT_TYPE",
"CACHE_MODE_FORCE_CACHE_ALL"
]
},
"httpRequest": {
"requestMethod": "GET",
"requestUrl": "http://YOUR_LB_IP/test.txt",
"requestSize": "85",
"status": 200,
"responseSize": "682",
"userAgent": "curl/8.5.0",
"remoteIp": "YOUR_CLIENT_IP",
"cacheHit": true,
"cacheLookup": true,
"latency": "0.002400s",
"protocol": "HTTP/1.1"
},
"resource": {
"type": "http_load_balancer",
"labels": {
"url_map_name": "cdn-url-map",
"backend_service_name": "cdn-backend-bucket",
"forwarding_rule_name": "cdn-forwarding-rule",
"zone": "global",
"target_proxy_name": "cdn-http-proxy",
"project_id": "YOUR_PROJECT_ID"
}
},
"timestamp": "2026-07-05T20:54:05.024170Z",
"severity": "INFO",
"logName": "projects/YOUR_PROJECT_ID/logs/requests",
"trace": "projects/YOUR_PROJECT_ID/traces/c01d7bdb9b4141468a888c28c6e9e49a",
"receiveTimestamp": "2026-07-05T20:54:05.816635782Z",
"spanId": "101ccc4461061969"
}
cacheHit: true と statusDetails: "response_from_cache" が確認できれば、Cloud CDN のキャッシュが正常に機能しています。
まとめ
今回の GA により、Cloud CDN および外部 Application Load Balancer から Cloud Storage のプライベートバケットへセキュアにアクセスできるようになりました。バケットを公開せずに静的コンテンツを配信できるため、機密性の高い画像・動画・ファイルを CDN で配信したい場合のアーキテクチャ選択肢が広がります。
設定の仕組みは IAM による権限付与のみで完結しており、署名付き URL のような期限管理が不要です。Google が管理するサービスアカウント(service-PROJECT_NUM@https-lb.iam.gserviceaccount.com)に roles/storage.objectViewer を付与するだけで有効になります。ただし、Regional external Application Load Balancer では現時点でサポートされていない点と、読み取り専用であるという制約に注意してください。
複数バケットをプライベートに配信したい場合は、バケットごとに同じ手順で roles/storage.objectViewer を付与してください。サービスアカウントはプロジェクト単位で1つ共用されます。
静的コンテンツをセキュアに CDN で配信したいユースケースがある場合は、ぜひ試してみてください。
この記事が誰かの助けになれば幸いです。
以上、クラウド事業本部コンサルティング部の渡邉でした!







