Amazon CloudFrontのオリジンアクセスコントロール(Origin Access Control、OAC)は、S3バケットを一般公開しないまま、CloudFront経由のリクエストにだけコンテンツを返すための仕組みです。CloudFrontがオリジンへのリクエストにSigV4署名を付け、S3側はバケットポリシーで「CloudFrontサービスプリンシパルからの、指定したディストリビューション経由のリクエスト」だけを許可します。2022年8月25日に、従来のオリジンアクセスアイデンティティ(OAI)の後継としてAWSが公開しました。この記事では、OACとOAIの機能差、バケットポリシーと署名動作の具体的な設定、SSE-KMS暗号化オブジェクトの配信、OAIからの無停止移行、そして最も詰まりやすい403 Access Deniedの切り分けまでを、AWS公式ドキュメントの記述に沿って整理します。
まとめ:OACの要点とOAIとの違い
新規にS3オリジンを保護するならOAC一択で、OAIを選ぶ理由はありません。AWSは開発者ガイドで「OACの使用を推奨する(We recommend that you use OAC instead)」と明記し、その根拠として全リージョンのS3バケット対応・SSE-KMS対応・S3への動的リクエスト対応の3点を挙げています。
| 観点 | OAC | OAI(レガシー) |
|---|---|---|
| AWSの位置づけ | 推奨 | legacy, not recommended |
| 対応リージョン | 全リージョン | オプトイン・2023年1月以降の新設は非対応 |
| SSE-KMS暗号化オブジェクト | 対応 | 非対応 |
| S3への動的リクエスト | 対応(PUT・DELETE) | 非対応 |
| プリンシパルの指定 | サービスプリンシパル+SourceArn条件 | OAI固有のIAM ARN |
| アカウントあたりの上限 | 100 | 100 |
設定でつまずく箇所はほぼ3つに集約されます。バケットポリシーのAWS:SourceArnにディストリビューションIDを正しく書くこと、S3のオブジェクト所有権を「バケット所有者の強制」にしておくこと、そしてサブディレクトリのURLが403になる仕様を理解しておくことです。この3点を、公式ガイドの記述と設定例で順に確認します。
Origin Access Control(OAC)の役割とS3を非公開のまま配信する仕組み
S3バケットをそのまま公開してCDNを前段に置くと、CloudFrontのドメインとS3のURLの両方からコンテンツに到達できてしまいます。キャッシュを迂回されるだけでなく、WAFや地理的制限といったCloudFront側の制御もすべて素通りされます。OACはこの二重の入口をふさぐための機能です。
動作は単純で、CloudFrontがオリジンへ転送するリクエストにSignature Version 4(SigV4)の署名を付け、S3バケット側はバケットポリシーでその署名済みリクエストだけを許可します。バケットのパブリックアクセスはすべてブロックしたままにでき、S3のREST APIエンドポイントへ直接アクセスした利用者に返るのは403です。OACの実体はCloudFront側のリソース(AWS::CloudFront::OriginAccessControl)で、名前・オリジンタイプ・署名プロトコル・署名動作を持ちます。
対応するオリジンはS3だけではありません。開発者ガイドの「Restrict access to an AWS origin」は、AWS Elemental MediaPackage v2、AWS Elemental MediaStore、Lambda関数URLもOACで保護できるオリジンとして挙げています。一方、ALBやEC2をプライベートのまま公開したい場合の管轄はCloudFront VPCオリジンという別の機能です。CloudFront自体の役割やエッジ機能の全体像はAmazon CloudFrontの解説記事で扱っています。
OACとOAI(オリジンアクセスアイデンティティ)の違い
AWSが挙げる3つの機能差
開発者ガイドはOACを推奨する理由として、次の3点を挙げています。いずれもOAIでは「未対応、または追加の回避策が必要(OAI doesn’t support these features or it requires extra workarounds)」とされる項目です。
- すべてのAWSリージョンのS3バケット。2022年12月以降に追加されたオプトインリージョンを含みます(ガイドのOAI側の記述では「2023年1月以降に追加された新リージョン」と表記されており、時期の書き方に揺れがあります)
- AWS KMSによるサーバー側暗号化(SSE-KMS)で暗号化されたオブジェクト
- S3への動的リクエスト。ガイドのOAC推奨理由では
PUTとDELETE、OAI側の制約説明ではPUT・POST・DELETEが挙げられています
3点目は、CloudFrontのキャッシュビヘイビアで許可するHTTPメソッドの設定と組み合わせて考える必要があります。CloudFrontが選べるのは「GET, HEAD」「GET, HEAD, OPTIONS」「GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE」の3択で、ガイドは2つ目または3つ目を選ぶ場合について「PUTを使いたいという理由だけでこれらのメソッドを許可した場合でも、DELETEリクエストを適切に扱うようS3バケットポリシーを構成しなければならない」と警告しています。読み取り専用の配信なら「GET, HEAD」に絞り、バケットポリシーもs3:GetObjectだけに留めるのが安全です。
新規はOAC、既存OAIを残すかの判断基準
既存のOAI構成をすぐ壊す必要はありません。OAIは削除されておらず、CloudFrontコンソールにも「Identities (legacy)」として残っています。ただし移行を先送りしてよいのは、静的コンテンツを読み取り専用で配信していて、暗号化がSSE-S3、リージョンも東京や大阪のような既存リージョンに閉じている場合に限られます。SSE-KMSを使う要件が出た時点、あるいは新設のオプトインリージョンにバケットを置く時点で、OAIでは構成そのものが組めません。
暗号化要件は監査やセキュリティレビューの都合で後から降ってくることが多く、その段階で移行するとバケットポリシーとディストリビューションを本番稼働中に触ることになります。新規構築でOAIを選ぶ判断は、この将来コストを自分で作りに行くだけなので推奨できません。
OACの設定手順(バケットポリシーと署名動作)
S3バケットポリシーによるCloudFrontサービスプリンシパルの許可
OACを作成する前に、まずS3側にCloudFrontからのアクセスを許可します。プリンシパルはOAI時代のようなOAI固有のIAM ARNではなく、サービスプリンシパルcloudfront.amazonaws.comです。これだけでは同じサービスプリンシパルを使う他アカウントのディストリビューションからも到達しうるため、ConditionでAWS:SourceArnにディストリビューションのARNを指定して、特定のディストリビューション経由のリクエストだけに絞ります。読み取り専用の場合のポリシーは次の形です。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCloudFrontServicePrincipalReadOnly",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::amzn-s3-demo-bucket/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::111122223333:distribution/EDFDVBD6EXAMPLE"
}
}
}
]
}
ビューワーからのアップロードを通す構成ではActionにs3:PutObjectを追加します。あわせて、S3のオブジェクト所有権を「バケット所有者の強制(Bucket owner enforced)」にしておく必要があります。新規バケットの既定値がこれにあたるため通常は意識不要ですが、ACLが必要な場合は「バケット所有者を優先(Bucket owner preferred)」を使う、というのがガイドの指示です。バケット自体の設計はAmazon S3の仕組みとストレージクラスの解説もあわせて確認してください。
OACの作成とディストリビューションへの適用
コンソールでは、CloudFrontのナビゲーションペインで「Origin access」から「Create control setting」を選び、名前を付けてオリジンタイプにS3を指定します。作成後、対象ディストリビューションの「Origins」タブで該当オリジンを編集し、「Origin access」に「Origin access control settings (recommended)」を選んで作成したOACを紐付けてください。保存するとディストリビューションが全エッジロケーションへ展開され、新しい設定を受け取ったエッジからS3へのリクエストに署名が付き始めます。
CloudFormationで管理する場合のテンプレートは次のとおりです。SigningBehaviorをalways、SigningProtocolをsigv4にします。
Type: AWS::CloudFront::OriginAccessControl
Properties:
OriginAccessControlConfig:
Description: An optional description for the origin access control
Name: ExampleOAC
OriginAccessControlOriginType: s3
SigningBehavior: always
SigningProtocol: sigv4
AWS CLIから作る場合は、スケルトンを出力して編集する手順が公式に案内されています。出力されたIdを控えておき、ディストリビューション側のオリジンのOriginAccessControlIdに設定します。
aws cloudfront create-origin-access-control --generate-cli-skeleton yaml-input > origin-access-control.yaml
aws cloudfront create-origin-access-control --cli-input-yaml file://origin-access-control.yaml
既存ディストリビューションへ後付けする場合は、次の2コマンドの間で構成ファイルを編集します。
aws cloudfront get-distribution-config --id EDFDVBD6EXAMPLE --output yaml > dist-config.yaml
aws cloudfront update-distribution --id EDFDVBD6EXAMPLE --cli-input-yaml file://dist-config.yaml
編集内容は3点です。オリジン内のOriginAccessControlIdにOACのIdを入れる、OriginAccessIdentityの値が残っていれば空にする、ETagフィールドを値そのままでIfMatchにリネームする。最後のリネームは見落としやすく、忘れると更新が通りません。
署名動作(Signing behavior)3種の選択基準
OACには署名動作の設定があり、コンソールの表示とAPI上の値は次のように対応します。特別な要件がなければ推奨設定のまま使います。
| コンソール表示 | API・CLI・CFn | 用途 |
|---|---|---|
| Sign requests (recommended) | always | 常に署名する既定の構成 |
| Do not sign requests | never | OACを一括で無効化する |
| Do not override authorization header | no-override | ビューワーのAuthorizationヘッダーを尊重する |
ガイドが明示しているのはHTTPSの扱いです。CloudFrontとS3間の通信が常にHTTPSになるのは、常に署名する(always)を選んだ場合に限ると書かれています。neverやno-overrideを選ぶと、CloudFrontとオリジン間のプロトコルはビューワープロトコルポリシー等の設定に従うため、オリジン間もHTTPSにしたいなら「Redirect HTTP to HTTPS」または「HTTPS only」を併用する必要があります。
neverは「OACを一時的に外す」ためのスイッチであり、この設定を使うならS3バケット側をパブリックアクセス可能にしておかなければCloudFrontがオリジンに到達できません。no-overrideを選ぶ場合は、対象のS3オリジンを参照する全キャッシュビヘイビアのキャッシュポリシーにAuthorizationヘッダーを追加してください。忘れるとビューワーのAuthorizationヘッダーがオリジンに届きません。
SSE-KMSで暗号化したオブジェクトを配信するKMSキーポリシー
SSE-KMSで暗号化したオブジェクトをOAC経由で配信する場合、バケットポリシーだけでは足りません。KMSキーポリシーにCloudFrontサービスプリンシパルを許可するステートメントを追加します。ここを忘れると、バケットポリシーが正しくてもオブジェクト取得時に失敗します。以下は単体で完結したポリシーではなく、既存のKMSキーポリシーのStatement配列へ追加する1要素です。
{
"Sid": "AllowCloudFrontServicePrincipalSSE-KMS",
"Effect": "Allow",
"Principal": {
"Service": ["cloudfront.amazonaws.com"]
},
"Action": [
"kms:Decrypt",
"kms:Encrypt",
"kms:GenerateDataKey*"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::111122223333:distribution/EDFDVBD6EXAMPLE"
}
}
}
読み取りだけならkms:Decryptで足りますが、CloudFront経由のアップロードを許可する構成では暗号化側の権限も必要になるため、公式例は3つのアクションをまとめて許可しています。限定に使うのは、ここでもAWS:SourceArn条件です。OAIではこの構成自体が組めないため、SSE-KMSは実質的にOAC移行の最大の動機になります。
OAIからOACへ無停止で移行する手順
移行の要点は、切り替えの瞬間にCloudFrontがバケットへのアクセス権を失わないよう、先にバケットポリシーへ両方のプリンシパルを書いておくことです。ガイドが示す順序は次の3ステップです。
- バケットポリシーに、OAC用(サービスプリンシパル+SourceArn条件)とOAI用(OAIのIAM ARN)の2つのステートメントを並べて許可する
- ディストリビューションのオリジン設定をOAIからOACへ切り替え、全エッジへの展開完了を待つ
- 展開が完了してから、バケットポリシーのOAI許可ステートメントを削除する
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCloudFrontServicePrincipalReadOnly",
"Effect": "Allow",
"Principal": { "Service": "cloudfront.amazonaws.com" },
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::amzn-s3-demo-bucket/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::111122223333:distribution/EDFDVBD6EXAMPLE"
}
}
},
{
"Sid": "AllowLegacyOAIReadOnly",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity E1EXAMPLE1"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::amzn-s3-demo-bucket/*"
}
]
}
OAIのIDは、CloudFrontコンソールの「Security」から「Origin access」の「Identities (legacy)」で確認できます。APIならListCloudFrontOriginAccessIdentitiesです。ステップ2の展開完了を待たずにステップ3を実行すると、まだ旧設定を保持しているエッジからのリクエストが403になるため、順序は必ず守ってください。
OAC構成で403 Access Deniedが出るときの切り分け
OACの設定自体は数分で終わりますが、実際に時間を溶かすのは設定後の403です。開発者ガイドはS3オリジンで403が返る原因として、OAIまたはOACが有効でないままバケットが非公開になっている、URLのパスが正しくない、リクエストされたオブジェクトが存在しない、RESTエンドポイントに対してHostヘッダーが転送されている、カスタムエラーページを設定している、の5つを挙げています。厄介なのは、これらが同じ403として返るため区別がつかない点です。切り分けは次の順で進めると早く終わります。
サブディレクトリのindex.htmlが403になる仕様上の理由
ルートURLでは正しく表示されるのに、/install/のようなサブディレクトリのURLだけ403になる、という詰まり方があります。原因はデフォルトルートオブジェクトの仕様で、設定ミスではありません。ガイドは「デフォルトルートオブジェクトを定義しても、ディストリビューションのサブディレクトリへのリクエストではデフォルトルートオブジェクトは返されない」と明記しており、installディレクトリにindex.htmlのコピーが置かれていても返されません。
S3をウェブサイトエンドポイントとして使う場合はインデックスドキュメントがサブディレクトリでも機能します(ガイドは「インデックスドキュメントのコピーが各サブディレクトリに存在する必要がある」と補足しています)。ただしOACはRESTエンドポイント側の仕組みなので、この挙動を借りられません。結果として、CloudFrontはinstall/というキーを取りに行き、そのキーが存在しないS3が403(s3:ListBucketを許可していないため404ではなく403)を返します。
対処はCloudFront Functionsでビューワーリクエストのパス末尾にindex.htmlを付与するか、URLを常にファイル名まで含めた形で発行するかのどちらかです。デフォルトルートオブジェクトにはパス形式(install/index.html)も指定できますが、その効き方についてはガイド内で記述が割れています。動作説明の項は「パスを指定した場合、CloudFrontはinstallディレクトリへのリクエストに対してデフォルトルートオブジェクトを返す」と記す一方、設定手順の項は「パスを設定するとルートURLへのリクエストがそのパスのファイルを返す」と書いています。いずれの解釈でもパス形式で救えるのは1ディレクトリだけなので、階層が複数あるサイトではCloudFront Functionsでの付与が確実です。関数のコードと公開・関連付けのコマンドはCloudFrontとS3で静的サイトを公開する手順に載せています。
バケットポリシー・オブジェクト所有権・ウェブサイトエンドポイントの構成ミス
サブディレクトリ以外の403は、次の3点を上から順に確認すると大半が解決します。
- バケットポリシーの
AWS:SourceArnが、実際に使っているディストリビューションのIDと一致しているか。別環境のIDをコピーしたまま貼るミスが起きやすい箇所です - S3のオブジェクト所有権が「バケット所有者の強制」になっているか。ACLベースの古いバケットを流用すると条件を満たしません
- オリジンドメインにS3のウェブサイトエンドポイントを指定していないか。ウェブサイトエンドポイントを使う構成はカスタムオリジン扱いとなり、ガイドは「OAC(およびOAI)を使えない」と明記しています
SSE-KMSを使っている場合はKMSキーポリシーの許可漏れが4つ目の候補になります。またAWS WAFをディストリビューションに関連付けていると、WAFがブロックした403とオリジンが返した403をCloudFrontが区別できません。WAFを使っている環境では、まずWeb ACLのブロックログを確認してからS3側の調査に進むほうが速く終わります。
OACを使えない構成と、VPCオリジンとの使い分け
使えない構成を設計の前に把握しておくと、やり直しを避けられます。ディレクトリごとのインデックスドキュメントやリダイレクトルールといったS3ウェブサイトホスティング固有の機能が要件に入っているなら、前述のとおりOACは選べません。CloudFront FunctionsやLambda@Edgeで同等の挙動を作る設計に寄せる判断が必要です。ただしガイドは、OACがLambda@Edgeによるオリジンリダイレクトに対応しないことも明記しています。
オリジンがALBやEC2の場合もOACの対象外です。この用途に用意されているのはVPCオリジンで、対応するオリジンの種類が根本的に違います。API GatewayをCloudFrontの背後に置く構成も同様に、CloudFrontとAPI Gateway、Lambdaを連携させる構成で扱う設計判断になります。
廃止時の順序にも注意が必要です。OACを紐付けたディストリビューションとS3バケットを両方消すときは、先にディストリビューションを削除します。ガイドはこの順序を示したうえで、代替としてオリジンドメイン名にリージョンを含める方法、それも取れない場合はOACを外して公開設定へ切り替えてから削除する方法を挙げています。削除済みバケット名は他者が再取得できるため、逆順にすると意図しないオリジンを指す状態が生じ得る、というのが順序を守る実務上の理由です。
よくある質問
OACとは何の略ですか?
Origin Access Control(オリジンアクセスコントロール)の略です。CloudFrontがオリジンへ送るリクエストにSigV4署名を付け、オリジン側で「CloudFront経由の認証済みリクエストだけ」を許可するためのCloudFront機能を指します。医療分野の経口抗凝固薬(Oral Anticoagulant)や陸上競技クラブの略称としても同じ綴りが使われますが、AWSの文脈では常にオリジンアクセスコントロールを指します。
OAIは今後使えなくなりますか?
2026年8月時点で廃止は告知されておらず、既存のOAI構成は動作します。ただしAWSは開発者ガイドでOAIを「legacy, not recommended」と位置づけ、コンソールでも「Identities (legacy)」の見出しへ移しました。新規構築でOAIを選ぶ理由はなく、SSE-KMSや新リージョンの要件が出た時点でOACへの移行が必須になります。
OACはS3以外のオリジンでも使えますか?
使えます。ただしOACはオリジン種別ごとに作り分けます。CloudFormationのOriginAccessControlOriginTypeが受け付ける値はs3・mediastore・lambda・mediapackagev2の4つに限られており(正規表現^(s3|mediastore|lambda|mediapackagev2)$)、S3用に作ったOACをLambda関数URLへ流用することはできません。SigningProtocolに指定できる値はsigv4のみです。ALBやEC2といったVPC内のリソースはOACの対象外で、VPCオリジンの管轄になります。
OACは1つのAWSアカウントでいくつ作成できますか?
既定のクォータは1アカウントあたり100です。1つのOACに関連付けられるディストリビューションも100が既定値で、どちらもService QuotasまたはAWS Supportから引き上げを申請できます。OACは複数のディストリビューションで共有できるため、通常の運用で既定値に達することはまれです。
OACを設定してもS3のURLへ直接アクセスできてしまうのはなぜですか?
バケットのパブリックアクセス設定が残っているか、以前のパブリック読み取り許可のステートメントがバケットポリシーに残っているケースが大半です。OACはCloudFront側に署名を付けさせる機能であり、S3側の公開設定を自動的に閉じるわけではありません。ブロックパブリックアクセスを有効にし、バケットポリシーがCloudFrontサービスプリンシパルへの許可だけになっているかを確認してください。