目次
10 章

S3 — 静的ホスティング, presigned URL

AWS で最も古いオブジェクトストレージ S3。バケットの形と名前のグローバル一意性、ポリシーと Public Access Block、静的サイトホスティング、presigned URL、そしてストレージクラスで費用を下げるパターンまで整理します。

第8章 ~ 第9章 の EC2 がコンピューティング領域なら、S3 (Simple Storage Service) は AWS のオブジェクトストレージ領域です。2006年に AWS の最初のサービスとしてリリースされた最も古いサービスであり、今も最もよく使われるサービスの1つです。

S3 は事実上、無限大容量のグローバルファイルシステムです(ただしディレクトリは偽物です)。11 9’s (99.999999999%) の耐久性、GB あたり約 $0.023 の価格、他のすべての AWS サービスのデータハブ — この3つが S3 の正体です。

本章では S3 の構造から始め、ポリシーとセキュリティ、静的ホスティング、presigned URL、ストレージクラスまで一つの流れで整理します。ここで扱う静的ホスティングは 第14章 CloudFront で HTTPS と Edge キャッシュを加えて完成し、S3 PUT トリガーは 第17章 Lambda 基礎 のイベント処理へつながります。

バケットとオブジェクト #

S3 は覚えるべき核心が2つだけです。

  • バケット (Bucket) — オブジェクトを入れるコンテナです。1アカウント / リージョン単位で作ります。
  • オブジェクト (Object) — 実際のファイルです。キー(key)で識別します。
S3 の形
my-bucket/                       ← バケット (名前はグローバル一意)
  images/
    profile/2026/avatar-001.jpg  ← オブジェクト (key = 全体パス)
    profile/2026/avatar-002.jpg
  videos/
    intro.mp4
  index.html

ディレクトリは実はありません。上の図の / はキーの一部です。images/profile/2026/avatar-001.jpg がオブジェクト1つの全体キー(key)です。コンソールが / を基準にフォルダのように見せているだけです。

バケット名のグローバル一意性 #

バケット名は全世界の AWS アカウントすべてを横断して一意でなければなりません。my-bucket のような平凡な名前は、すでに誰かが取っています。

安全なバケット名
my-company-dev-uploads-2026
acme-prod-static-ap-northeast-2

ルールは次のとおりです。

  • 3 ~ 63 文字、小文字 / 数字 / - / . を使います。
  • ドット(.)は可能ですが、SSL 証明書のワイルドカードで問題になるため、通常は - だけを使います。
  • IP アドレスの形は不可で、xn-- で始められません(Punycode)。
  • 大文字とアンダースコアは不可です。

名前に環境 / 用途 / リージョン / 会社名を書いておくと、請求書と検索が楽になります。

バケットはリージョン単位 #

バケット名はグローバル一意ですが、データは1つのリージョンに住んでいます。ap-northeast-2 (ソウル) に作れば、オブジェクトデータはソウルのデータセンターの中にあります。コンソールに入るとどのリージョンにあるかが見えます。このグローバル名 / リージョンデータの区別は、第1章 AWS 入門 のグローバルサービス vs リージョンサービスの議論と同じ文脈です。

リージョン間の複製は S3 Replication (CRR, Cross Region Replication) で明示的に設定します。

オブジェクトの核心属性 #

オブジェクト1つは次を持ちます。

属性役割
Keyオブジェクトの全体パス。バケットの中で一意
Body実際のデータ (最大 5TB)
Content-Typeブラウザがどう処理するか (image/jpeg, application/json)
Metadataユーザー定義ヘッダー (x-amz-meta-*)
ACLオブジェクト単位の権限 (最近はほぼ使わず、バケットポリシーで代替)
Storage Classストレージクラス (Standard, IA, Glacier など)
Version IDバージョニングを ON にしていればバージョン識別子
ETagコンテンツハッシュ (大部分は MD5)

コンソール / CLI / SDK でアップロード #

aws cli でアップロード / ダウンロード
# 単一ファイル
aws s3 cp ./image.jpg s3://my-bucket/images/profile/avatar.jpg

# フォルダ全体を同期
aws s3 sync ./public s3://my-bucket --delete

# Content-Type を明示
aws s3 cp ./index.html s3://my-bucket/ --content-type "text/html; charset=utf-8"

# ダウンロード
aws s3 cp s3://my-bucket/data.json ./
Python (boto3)
import boto3

s3 = boto3.client("s3")
s3.upload_file("image.jpg", "my-bucket", "images/avatar.jpg")
s3.download_file("my-bucket", "data.json", "data.json")

セキュリティの4つの構成 #

S3 のセキュリティは複数の層が重なって動きます。評価順は上から下へ行くほど弱くなります。

S3 権限の評価順
1. Public Access Block      ← 最も強力。遮断の決定がすべての上に
2. SCP (Organizations)      ← アカウント単位のガード
3. IAM Policy               ← ユーザー / ロール単位
4. Bucket Policy            ← バケット単位
5. Object ACL               ← オブジェクト単位 (旧方式、ほぼ使わない)

Public Access Block — 最初に #

Public Access Block (PAB) はバケットが誤って公開されるのを防ぐ安全装置です。4つのオプションがあります。

オプション意味
BlockPublicAcls新しい ACL が public になれないように
IgnorePublicAcls既存の public ACL を無視
BlockPublicPolicy新しいバケットポリシーが public になれないように
RestrictPublicBucketsすでに public のバケットも IAM Principal だけがアクセス

最近はすべての新しいバケットがアカウントレベルで4つすべてを ON にするのがデフォルトです。静的ホスティングのように、わざと public なバケットだけを明示的に解除します。

アカウントレベルの PAB を ON にする
aws s3control put-public-access-block \
  --account-id 123456789012 \
  --public-access-block-configuration \
    BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true

Bucket Policy — JSON ポリシー #

Bucket Policy はバケットに直接付ける JSON ポリシーです。誰が(Principal)何を(Action)どこに(Resource)できるかを定義します。

ALB ログを受けるバケットポリシーの例
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "logdelivery.elasticloadbalancing.amazonaws.com"
      },
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::my-alb-logs/*",
      "Condition": {
        "StringEquals": {
          "s3:x-amz-acl": "bucket-owner-full-control"
        }
      }
    }
  ]
}
アプリ IAM Role だけに読み取りを許可
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:role/MyAppRole"
      },
      "Action": ["s3:GetObject", "s3:ListBucket"],
      "Resource": [
        "arn:aws:s3:::my-bucket",
        "arn:aws:s3:::my-bucket/*"
      ]
    }
  ]
}

IAM Policy #

IAM ユーザー / ロール に付けるポリシーです。Bucket Policy と結合して効果を生みます。cross-account の場合は両方を通過してこそ Allow が適用されます。

アプリ IAM Role ポリシー
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": ["s3:GetObject", "s3:PutObject"],
    "Resource": "arn:aws:s3:::my-bucket/uploads/*"
  }]
}

詳しい IAM 設定は 第2章 IAM で扱います。

静的サイトホスティング #

S3 は静的な HTML / CSS / JS をそのままホスティングできます。最も簡単な静的サイトホスティング方式です。

バケットの静的ホスティングを有効化
aws s3 website s3://my-static-site/ \
  --index-document index.html \
  --error-document 404.html

このコマンドの後、バケットが次の URL で応答します。

S3 website endpoint
http://my-static-site.s3-website-ap-northeast-2.amazonaws.com

公開アクセスの許可 #

デフォルト値では PAB が塞ぎます。静的ホスティングは意図された public なので、次を行います。

  1. バケット PAB で BlockPublicPolicy の2項目を解除します。
  2. Bucket Policy で全員に GetObject を許可します。
静的ホスティング用 public read ポリシー
{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "PublicReadGetObject",
    "Effect": "Allow",
    "Principal": "*",
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::my-static-site/*"
  }]
}

S3 静的ホスティングの限界 #

S3 だけでは次ができません。

  • HTTPS — S3 website endpoint は HTTP です。
  • カスタムドメイン + SSL 証明書 — 直接はできません。
  • Edge キャッシュ — 全世界の速い応答ができません。

そのため運用ではほぼ常に S3 + CloudFront パターンを使います(第14章 CloudFront で扱います)。そのときは PAB も再び ON にして、OAC で CloudFront だけにアクセスを許可するパターンになります。

Presigned URL — 一時的な権限 #

Presigned URL は、このオブジェクトを N 分間だけ誰でもダウンロードまたはアップロードできるという一時的な権限を作る方式です。権限のないユーザーに権限を一時的に委任するパターンです。

最もよくある使いどころは次のとおりです。

  • ユーザープロフィール画像のアップロード — クライアントが直接 S3 へ PUT します。
  • 決済領収書のダウンロード — 5分のリンクです。
  • 非公開ビデオストリーミング — 1時間のトークンです。
presigned PUT URL を作る (boto3)
import boto3

s3 = boto3.client("s3")
url = s3.generate_presigned_url(
    "put_object",
    Params={
        "Bucket": "my-bucket",
        "Key": f"uploads/user-123/{filename}",
        "ContentType": "image/jpeg",
    },
    ExpiresIn=600,  # 10 分
)
# クライアントはこの URL で PUT リクエスト
curl で presigned URL を使う
curl -X PUT --upload-file ./photo.jpg "<presigned-url>"

presigned URL のセキュリティ #

  • URL 自体に一時的な資格情報が含まれています。その URL さえあれば誰でも使えます。
  • 有効期限が過ぎると自動で無効になります。
  • HTTPS だけを使います。HTTP で漏れると露出します。
  • ContentType / Content-Length のような条件を一緒に埋め込めます。

POST フォーム vs PUT URL #

アップロード方式は2つです。

  • PUT URL — 単純です。ヘッダーでメタデータを渡し、ContentType を1つ固定します。
  • POST フォーム (presigned post) — 複雑です。多重条件(content-length-rangestarts-with など)でより安全です。

大規模だったり重要なアップロードは POST フォームが推奨されます。単純な場合は PUT URL で十分です。

バージョニングとライフサイクル #

Versioning — オブジェクトの履歴 #

バケットに Versioning を ON にすると、同じキーに何度も PUT したときに以前のバージョンが自動で保存されます。

Versioning を ON にする
aws s3api put-bucket-versioning \
  --bucket my-bucket \
  --versioning-configuration Status=Enabled

ON にした後は次のとおりです。

  • Delete も実際には消しません。Delete Marker だけが追加されます。
  • 以前のバージョンは --version-id で復旧できます。
  • 保存費用はすべてのバージョンを合算します(落とし穴)。

Lifecycle — 自動整理 / 移行 #

古いオブジェクトを自動で安いクラスへ移すか削除するルールです。

Lifecycle ルールの例
{
  "Rules": [{
    "ID": "ArchiveOldLogs",
    "Status": "Enabled",
    "Filter": { "Prefix": "logs/" },
    "Transitions": [
      { "Days": 30,  "StorageClass": "STANDARD_IA" },
      { "Days": 90,  "StorageClass": "GLACIER" }
    ],
    "Expiration": { "Days": 365 }
  }]
}

運用にライフサイクルはほぼ必須です。なければ数か月後に請求書が怖くなります。

ストレージクラス — 費用の項目 #

同じデータをどれだけ頻繁に、どれだけ速く取り出すかによって別のクラスに置けば、費用が大きく節約できます。

クラスGB/月頻繁にアクセス取り出し時間役割
Standard$0.023毎日即時デフォルト。ホットデータ
Standard-IA$0.0125たまに即時バックアップ、分析データ
One Zone-IA$0.01たまに、再生成可能即時1 AZ のみ — 重要度低い
Intelligent-Tiering自動パターン不明即時アクセス頻度がばらつく
Glacier Instant Retrieval$0.004四半期に1回即時アーカイブ + たまに即時必要
Glacier Flexible Retrieval$0.0036年に1~2回分~時間一般的なアーカイブ
Glacier Deep Archive$0.00099ほぼなし12 時間長期コンプライアンス

数字は ap-northeast-2 基準のおおよその値です。詳しくは 公式の価格表 を見ます。

クラス決定ガイド #

決定木
このデータ、毎日 / 毎週見るか?
├── YES → Standard
└── NO →
    たまに (月1回以下) ?
    ├── YES → Standard-IA  (再生成可能なら One Zone-IA)
    └── NO →
        パターンを予測できるか ?
        ├── YES → Glacier 系
        └── NO  → Intelligent-Tiering

落とし穴 — クラス移行の費用 #

Standard から IA のような移行ごとに、オブジェクトあたり約 $0.01 の小さな費用がかかります。オブジェクトが1億個なら、この費用が大きくなります。ライフサイクルで頻繁に移してはいけません。オブジェクトのサイズと頻度を見て決めます。

S3 の一貫性 #

昔は read-after-write の一貫性が弱かったです。2020年12月からすべてのリージョンで strong consistency が保証されます。

  • PUT 後の GET が即時に可能です。
  • DELETE 後の LIST が即時に反映されます。

ただしバージョンオブジェクトやメタデータの変更は、依然として若干の時間差があり得ます。

S3 と他のサービスの役割 #

一緒によく使うサービスパターン
CloudFrontS3 + Edge キャッシュ + ユーザードメイン (第14章)
LambdaS3 PUT トリガーで画像変換 / インデックス (第17章)
AthenaS3 の CSV / Parquet / JSON を SQL で
GlueS3 のデータカタログ / ETL
CloudTrail / VPC Flow Logs / ALB Logsすべて S3 に保存

よく出会う落とし穴 #

  • バケットが意図せず public — ニュースに出るデータ漏洩の半分は S3 です。新しいバケットは PAB 4つをすべて ON にした状態で始め、静的ホスティングのように意図された場合だけ明示解除します。
  • 費用爆弾 — GB あたりの保存 + リクエスト数 + データ転送の三重課金です。特にインターネットへ出るトラフィック(Egress)が GB あたり約 $0.09 なので、人気の静的サイトはこれが大きいです。CloudFront と束ねて Egress を削減し、Edge キャッシュで加速します(第14章)。
  • 小さいファイルが数百万個 — オブジェクト1つ1つに GET / PUT 費用があるため、小さいファイル数百万個のパターンは費用が意外に大きいです。束ねて(tar.gz、Parquet)保存するか、別のストレージへ移すのが答えです。
  • Lifecycle なしで1年 — ログや一時ファイルが Standard にそのまま残ると、数か月後に請求書が暴増します。ライフサイクルはバケットを作る初日に一緒に設定します。
  • Versioning を ON にして忘れる — Versioning を ON にして lifecycle がなければ、保存費用が無限に増加します。ON にしたら、ライフサイクルで古い非現行バージョンを整理します。
  • presigned URL の有効期限が長すぎる — 24時間の presigned URL は事実上、永久アクセスです。通常 5 ~ 15 分、長くても1時間に設定します。
  • s3:* ワイルドカード IAMAction: "s3:*" ポリシーは危険です。少なくとも GetObject / PutObject / ListBucket のように明示します。

練習問題 #

  1. §「S3 権限の評価順」の5つの層のうち、静的ホスティングバケットをわざと公開するにはどの層をどう解除すべきかを書いてみてください。そして 第14章 CloudFront の OAC パターンでは同じバケットの PAB をなぜ再び ON にするのか、一文で対比して説明してみてください。
  2. presigned PUT URL の ExpiresIn を 600 秒に設定したコードを見て、有効期限が長すぎるときと短すぎるときにそれぞれどんな問題が生じるかを、§「presigned URL のセキュリティ」を根拠に書いてみてください。
  3. ログバケットを1つ想定し、30日後 Standard-IA、90日後 Glacier、365日後に削除する Lifecycle ルールを自分で作成してみてください。このルールが §「費用爆弾」のどの項目を減らしてくれるかを、第27章 コスト最適化 と結びつけてメモしておいてください。

一行まとめ: S3 はバケット(グローバル一意の名前)とオブジェクト(key)の2概念だけがある無限オブジェクトストレージで、ディレクトリは偽物。セキュリティは PAB が最上位で、新しいバケットは PAB 4つをすべて ON にする。静的ホスティングは HTTPS と Edge がないので CloudFront と束ね、presigned URL は 5~15分の一時的な権限委任、Versioning は必ず Lifecycle と一対で置く。

次の章 #

オブジェクト領域はつかめました。次の 第11章 RDS では、リレーショナル DB の番に移ります。RDS のマネージドモデル、自動バックアップと PITR、Multi-AZ、パラメータ / オプショングループ、そしてマイナー vs メジャーアップグレードをどう扱うかを整理します。

X