【AWS SES】既存S3を使ってHTMLメールに画像を表示する[〜S3 + CloudFront + HTTPS(OAC対応・実務ベストプラクティス)〜]

HTMLメールを AWS SES で送信する際、
画像をどのように配信するかは、表示率・到達率・セキュリティに大きく影響します。

本記事では、

  • 既存の S3 バケットを流用

  • S3 は非公開のまま

  • CloudFront + HTTPS で画像配信

  • Python + Lambda で SES 送信

という、実務で最も事故が少ない構成を、具体的な手順付きで解説します。


なぜ「S3 + CloudFront + HTTPS」が最適なのか

HTMLメールでは、次の条件を満たす必要があります。

  • ほぼすべてのメールクライアントで画像が表示される

  • セキュリティ警告が出ない

  • スパム判定を受けにくい

  • 運用・更新が容易

これらを満たすのが、

S3(非公開)+ CloudFront(HTTPS)+ img src にURL指定

という構成です。


ゴール構成(全体像)

S3(既存・非公開)
 └ mail-assets/
    └ logo.png
        ↑
CloudFront(OAC)
 └ https://img.example.com/mail-assets/logo.png
        ↑
SES(HTMLメール)
  • S3 は 公開しない

  • CloudFront の Origin Access Control(OAC) で限定公開

  • HTMLメールでは HTTPS URL を指定するだけ


1. 既存S3バケットの準備

1-1. 画像配置(重要:Content-Type と Cache-Control)

aws s3 cp ./logo.png s3://my-existing-bucket/mail-assets/logo.v20260220.png \
  --content-type "image/png" \
  --cache-control "public, max-age=31536000, immutable"

ポイント

  • Content-Type
    → 誤ると一部環境で表示されないことがあります

  • Cache-Control
    → CloudFront キャッシュを最大活用

  • ファイル名バージョニング
    → 画像更新時のキャッシュ事故を防ぐ最善策


1-2. S3は公開しない(重要)

  • Block Public Access:ONのまま

  • CloudFront からのみアクセス可能にします(後述)


2. CloudFront を既存S3に紐付ける(OAC)

2-1. CloudFront ディストリビューション作成

Origin

  • Origin domain:既存S3バケット

  • Origin access:Origin Access Control(OAC)

Cache behavior

  • Viewer protocol policy:Redirect HTTP → HTTPS

  • Allowed methods:GET, HEAD

  • Cache policy:Managed-CachingOptimized


2-2. S3バケットポリシー(CloudFront専用)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowCloudFrontReadOnly",
      "Effect": "Allow",
      "Principal": { "Service": "cloudfront.amazonaws.com" },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-existing-bucket/mail-assets/*",
      "Condition": {
        "StringEquals": {
          "AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/EXAMPLEID"
        }
      }
    }
  ]
}

ポイント

  • S3は非公開のまま

  • CloudFront 特定ディストリビューションのみ許可

  • 既存用途がある場合も、mail-assets/ に限定すれば安全


3. HTTPS(独自ドメイン)設定(推奨)

3-1. ACM証明書(us-east-1)

CloudFront 用の証明書は us-east-1(バージニア北部) で発行します。

  • 例:img.example.com

  • DNS検証(Route53なら簡単)


3-2. CloudFront に証明書を設定

  • Alternate domain name:img.example.com

  • Custom SSL certificate:発行したACM証明書


3-3. Route53(Alias)

  • img.example.com → CloudFront distribution

独自ドメインを使うことで、
メール受信者からの 信頼性・見た目 が向上します。


4. HTMLメールの img タグ記述

<img
  src="https://img.example.com/mail-assets/logo.v20260220.png"
  alt="Your Service"
  width="200"
  style="display:block; max-width:100%; height:auto;"
>

実務ポイント

  • alt は必須(画像非表示・到達率対策)

  • CSSは インライン指定

  • width を指定してレイアウト崩れ防止


5. Lambda(Python)から SES でHTMLメール送信

5-1. IAM権限(最小)

{
  "Effect": "Allow",
  "Action": [
    "ses:SendEmail",
    "ses:SendRawEmail"
  ],
  "Resource": "*"
}

5-2. boto3 送信例

import boto3

ses = boto3.client("ses", region_name="ap-northeast-1")

def send_html_email(to_address: str) -> None:
    subject = "Welcome!"
    html_body = """
    <html>
      <body>
        <img src="https://img.example.com/mail-assets/logo.v20260220.png"
             alt="Your Service"
             width="200"
             style="display:block;">
        <p>こんにちは!</p>
      </body>
    </html>
    """

    ses.send_email(
        Source="no-reply@example.com",
        Destination={"ToAddresses": [to_address]},
        Message={
            "Subject": {"Data": subject, "Charset": "UTF-8"},
            "Body": {
                "Html": {"Data": html_body, "Charset": "UTF-8"},
                "Text": {"Data": "こんにちは!", "Charset": "UTF-8"}
            }
        }
    )

Textパート併記は到達率・アクセシビリティの観点で強く推奨されます。


6. よくあるトラブルと対策

画像が更新されない

  • 原因:CloudFront キャッシュ

  • 対策:ファイル名にバージョンを含める(最推奨)


AccessDenied(403)

  • OAC 用バケットポリシーの

    • Distribution ID

    • Account ID

    • パス指定
      を再確認


受信側で画像が表示されない

  • 受信者の設定による仕様

  • altテキスト+本文で内容が伝わる設計が重要


まとめ

  • HTMLメール画像は S3 + CloudFront + HTTPS が最適解

  • S3は 非公開のまま、OACで安全に配信

  • 画像更新は ファイル名バージョニング

  • HTMLは インラインCSS+alt必須

  • SES送信時は Textパート併記

HTMLメールの画像配信は、
Webではなく「メール特有の制約」を前提に設計することが最大のポイントです。


必要であれば、

  • CloudFormation / Terraform テンプレート

  • Gmail / Outlook で崩れない HTML 雛形

  • 開封率計測(トラッキングピクセル)設計

も記事化できます。