0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Cognito のメール送信、3 パターンで比較してみた ── デフォルト / カスタム From / SES 連携

0
Posted at

はじめに

Amazon Cognito は、サインアップ時の確認コードや仮パスワードをメールで送信できますが、このメール、デフォルトだと no-reply@verificationemail.com から届きます。

カスタムドメインの設定はできそうな感じはしてたのですが、いざ調べてみると設定方法がいくつかあるようでした。

そこで今回は、Cognito のメール送信設定を 3 パターン用意して、それぞれどんなメールが届くのか、メールヘッダーの認証結果まで含めて比較検証してみました。

この記事で学べること

  • Cognito 組み込みメール(COGNITO_DEFAULT)と SES 連携(DEVELOPER)の違い
  • CDK で各パターンを実装する方法
  • メールヘッダーの認証結果(SPF / DKIM / DMARC)がパターンごとにどう変わるか
  • COGNITO_DEFAULT モードの 1 日 50 通制限の実際の挙動

前提知識・条件

  • 検証日: 2026 年 4 月 8 日
  • リージョン: ap-northeast-1(東京)、AWS CDK(TypeScript)で構築
  • Route 53 でドメイン管理済み、SES はサンドボックス(本番解除なし)

CDK コードのリポジトリはこちらです。

Cognito 組み込みメール vs SES 連携、何が違う?

まず結論から。3 パターンの比較結果を先に記載します。

パターン From アドレス SPF DKIM DMARC 1 日の送信上限
1. デフォルト no-reply@verificationemail.com pass(amazonses.com) pass(verificationemail.com + amazonses.com) pass 約 50 通
2. Cognito 組み込み + カスタム From 任意のメールアドレス pass(amazonses.com) pass(amazonses.com) fail 約 50 通
3. SES 連携 + カスタムドメイン 任意@自ドメイン pass(mail.自ドメイン) pass(自ドメイン + amazonses.com) pass SES の制限に準拠

パターン2,3のCognito のメール送信モードには「COGNITO_DEFAULT」と「DEVELOPER」の 2 つがあります。

項目 COGNITO_DEFAULT(組み込み) DEVELOPER(SES 連携)
メール送信の仕組み Cognito が管理する SES リソースで送信 自分の AWS アカウントの SES で送信
送信制限 1 日あたり約 50 通 SES の制限に準拠(サンドボックス解除で大量送信可)
From アドレス デフォルトまたはカスタム カスタム
ドメイン検証 メールアドレス単位 ドメイン単位も可

では、早速それぞれのパターンを試してみます!

手順 1: デフォルト設定でメール送信

まずは何も設定しない素の状態から。

CDK でユーザープールを作って、メールがどう届くか見てみます。

CDK コード

cdk-stack.ts
export class Step1DefaultStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const userPool = new cognito.UserPool(this, 'UserPool1-Default', {
      userPoolName: 'UserPool1-Default',
      selfSignUpEnabled: true,
      signInAliases: { email: true },
      autoVerify: { email: true },
      removalPolicy: cdk.RemovalPolicy.DESTROY,
    });

    userPool.addClient('UserPool1-Client', {
      authFlows: { userPassword: true, userSrp: true },
    });
  }
}

特にメール関連の設定は書いていません。

email プロパティを省略すると、自動的に COGNITO_DEFAULT モードになります。

確認結果

デプロイ後、Cognito コンソールからユーザーを作成してみます。

CleanShot 2026-04-07 at 06.32.14.png

メールが届きました。From は no-reply@verificationemail.com です。

CleanShot 2026-04-07 at 06.33.45@2x.png

急にこのメールが来ると。「迷惑メール...?」ってなるやつです。

手順 2: Cognito 組み込み + カスタム From

次に、Cognito 組み込みモード(COGNITO_DEFAULT)のままで、From アドレスだけ変えてみます。

ただ、このパターンは CDK だけでは完結しませんでした。

Cognito が SES 経由でメールを送信するには、SES 側に「信頼ポリシー」が必要ですが、これに対応する CloudFormation リソースが存在しないためです。

コンソールで手動設定するか、AwsCustomResource で SES の PutIdentityPolicy API を呼ぶ必要があります。今回はコンソールから設定しました。

なお、CDK のコードでは L2 コンストラクト(UserPoolEmail.withCognito())だと From アドレスを変更できないため、L1(CfnUserPool)を使って sourceArn を指定しています。

SES での手動設定

まず、From にしたいメールアドレスを SES Identity に登録します。

SES コンソールを開いて、「ID」から「ID の作成」を選択。「ID の詳細」は「E メールアドレス」を選び、メールアドレスを入力して作成します。

CleanShot 2026-04-09 at 05.43.53@2x.png

すると、確認メールが届くので、メール内のリンクをクリックすると、検証完了画面が表示されます。

CleanShot 2026-04-07 at 06.43.46@2x.png

SES コンソールに戻ると、ステータスが「検証済み」になっているはずです。

CleanShot 2026-04-07 at 06.44.56@2x.png

次に、SES の「信頼ポリシー」を作成します。Cognito がそのアドレスから SES 経由でメールを送信するには、このポリシーが必要です。

SES コンソールで対象の Identity を選択し、「承認」タブから「ポリシーの作成」→「ポリシージェネレーター」を選択します。

  • 効果: 許可
  • プリンシパル: email.cognito-idp.amazonaws.com
  • アクション: ses:SendEmail, ses:SendRawEmail

CleanShot 2026-04-08 at 04.02.18.png

作成が完了すると、Identity の承認タブにポリシーが表示されます。

CleanShot 2026-04-08 at 04.03.28.png

注意: 本番環境では、Condition 要素で対象のユーザープール ARN を指定して、意図しないリソースからの送信を制限するようにしてください。詳しくは公式ドキュメントを参照してください。

https://docs.aws.amazon.com/ses/latest/dg/sending-authorization-identity-owner-tasks-policy.html

CDK コード(手順 2)

SES の手動設定が終わったら、CDK 側を書きます。

L2 コンストラクトでは対応できないため、L1(CfnUserPool)を使います。

cdk-stack.ts
interface Step2Props extends cdk.StackProps {
  customEmail: string;
}

export class Step2CognitoCustomStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: Step2Props) {
    super(scope, id, props);

    const sesEmailArn = `arn:aws:ses:${this.region}:${this.account}:identity/${props.customEmail}`;

    const userPool = new cognito.CfnUserPool(this, 'UserPool2-CognitoCustom', {
      userPoolName: 'UserPool2-CognitoCustom',
      autoVerifiedAttributes: ['email'],
      usernameAttributes: ['email'],
      adminCreateUserConfig: { allowAdminCreateUserOnly: false },
      emailConfiguration: {
        emailSendingAccount: 'COGNITO_DEFAULT',
        sourceArn: sesEmailArn,
        replyToEmailAddress: props.customEmail,
      },
    });

    new cognito.CfnUserPoolClient(this, 'UserPool2-Client', {
      userPoolId: userPool.ref,
      explicitAuthFlows: [
        'ALLOW_USER_PASSWORD_AUTH',
        'ALLOW_USER_SRP_AUTH',
        'ALLOW_REFRESH_TOKEN_AUTH',
      ],
    });
  }
}

emailConfigurationsourceArn に SES で検証したメールアドレスの ARN を指定することで、From アドレスがそのメールアドレスに変わります。

確認結果(手順 2)

デプロイ後、Cognito コンソールの認証方法を確認すると、E メールプロバイダーが「Cognito で E メールを送信」のまま、送信元の E メールアドレスが設定したアドレスに変わっています。

CleanShot 2026-04-08 at 04.27.39.png

ユーザーを作成してメールを送ってみると、、、届きました! From がカスタムアドレスになっています。

CleanShot 2026-04-08 at 04.17.04.png

メールヘッダーも確認し、認証周りの状況も確認します。
以下抜粋です。

Authentication-Results: mx.google.com;
  dkim=pass header.i=@amazonses.com header.s=suwteswk... header.b=BaoH6pSe;
  spf=pass smtp.mailfrom=...@ap-northeast-1.amazonses.com;
  dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=example-domain.click

認証結果を整理するとこうなります。

  • DKIM: pass ... ただし署名ドメインは amazonses.com(自ドメインではない)
  • SPF: pass ... ただし MAIL FROM は ap-northeast-1.amazonses.com
  • DMARC: fail ... From ドメインと DKIM / SPF のドメインが一致しない(アライメント不一致)

COGNITO_DEFAULT モードでは、From アドレスは変えられても、DKIM や SPF のドメインは amazonses.com のままです。そのため DMARC は fail します。

今回の gmail.com の DMARC ポリシーは p=NONE(DMARC fail でも何もしない)ですが、DMARC ポリシーが厳しいドメイン(p=REJECT など)を From にした場合は、受信拒否される可能性もあるため要注意です。

送信制限の検証

ついでに、COGNITO_DEFAULT モードの送信上限も検証してみました。

Gmail のエイリアス機能を使って連続送信したところ、50 通前後で以下のエラーが発生しました。

LimitExceededException: Exceeded daily email limit for the operation or the account.
If a higher limit is required, please configure your user pool to use your own
Amazon SES configuration for sending email.

エラーメッセージにも「SES 連携にしてね」と書いてありますね。

公式ドキュメント(Quotas in Amazon Cognito)にも 1 日あたり 50 通(調整不可)と明記されており、毎日 09:00 UTC にリセットされる仕様です。

手順 3: SES 連携 + カスタムドメイン

最後に SES 連携(DEVELOPER モード)です。メールアドレス単位ではなく、ドメイン単位で SES Identity を作成し、Cognito と連携させます。

SES ドメイン Identity の手動作成

SES ドメイン Identity は CDK で同時に作ることもできるのですが、UserPool が SES Identity を参照する際に DNS 検証が完了していないとエラーになるため、今回は事前に手動作成してから CDK でリソースを作成します。

CDK コード(手順 3)

cdk-stack.ts
interface Step3Props extends cdk.StackProps {
  customDomain: string;
}

export class Step3SESStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: Step3Props) {
    super(scope, id, props);

    const userPool = new cognito.UserPool(this, 'UserPool3-SES', {
      userPoolName: 'UserPool3-SES',
      selfSignUpEnabled: true,
      signInAliases: { email: true },
      autoVerify: { email: true },
      email: cognito.UserPoolEmail.withSES({
        fromEmail: `noreply@${props.customDomain}`,
        fromName: 'MyApp',
        sesVerifiedDomain: props.customDomain,
      }),
      removalPolicy: cdk.RemovalPolicy.DESTROY,
    });

    userPool.addClient('UserPool3-Client', {
      authFlows: { userPassword: true, userSrp: true },
    });
  }
}

手順 2 とは違い、L2 コンストラクトの UserPoolEmail.withSES() が使えます。
sesVerifiedDomain に検証済みドメインを指定し、fromEmail でそのドメインのメールアドレスを指定するだけ。シンプルですね。

また、SES はサンドボックス状態だと送信先も SES で検証済みのアドレスに限定されます。テスト時は送信先アドレスも SES Identity に登録しておく必要があります。(私は定期的にここでハマります。)

DMARC の設定

SES Identity にドメインを設定すると、DMARC の設定も可能になります。

Route 53 で管理しているドメインだとここから 1-click で DNS レコードの発行までしてくれました。

image.png

カスタム MAIL FROM ドメインの設定

デフォルトでは MAIL FROM が ap-northeast-1.amazonses.com のサブドメインになっています。このままだと SPF は amazonses.com で pass しますが、From ヘッダーの自ドメインとアライメントが一致しません。

そこでカスタム MAIL FROM ドメインを設定します。

SPF の検証ドメインが自ドメインのサブドメインになり、SPF ベースの DMARC アライメントも通るようになります。

設定は、ここから可能です。

image.png

今回は、mail をサブドメインに設定してみます。

image.png

設定後は、DMARC と同様に 1-click で DNS レコードの発行までしてくれました。

image.png

確認結果(手順 3)

デプロイ後、Cognito コンソールの認証方法を確認すると、E メールプロバイダーが「Amazon SES で E メールを送信」に変わり、送信者の名前に MyApp <noreply@自ドメイン> が表示されています。

CleanShot 2026-04-08 at 04.45.12.png

ユーザーを作成してメールを送ると、届きました! From が MyApp <noreply@自ドメイン> になっています。

CleanShot 2026-04-08 at 04.47.41.png

メールヘッダーの認証結果を見てみます。

Authentication-Results: mx.google.com;
  dkim=pass header.i=@example-domain.click header.s=jituvvea... header.b=SHRK7XWR;
  dkim=pass header.i=@amazonses.com header.s=suwteswk... header.b=S7rCtH+w;
  spf=pass smtp.mailfrom=...@mail.example-domain.click;
  dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=example-domain.click

問題なさそうですね。

  • DKIM: pass が 2 つ。@example-domain.click(自ドメイン)と @amazonses.com の両方で署名されている
  • SPF: pass。カスタム MAIL FROM 設定により MAIL FROM が mail.example-domain.click になり、From ドメインとアライメントも一致
  • DMARC: pass。DKIM・SPF ともに自ドメインでアライメントが一致

手順 2 では DKIM が amazonses.com だけで DMARC が fail していたのに対し、手順 3 では SPF / DKIM / DMARC すべて pass しています。

まとめ

Cognito のメール送信設定を 3 パターンで比較検証しました。

以下のような感覚を持ちました。

  • デフォルトは手軽だけど From が verificationemail.com で、サービスとしては使いにくい
  • Cognito 組み込み + カスタム From は CDK だけで完結せず、DMARC も fail する。50通制限も残る
  • SES 連携 + カスタムドメイン + DMARC + カスタム MAIL FROM で SPF / DKIM / DMARC すべて pass

本番で使うなら SES 連携一択っぽいですねー。

この記事が誰かのお役に立てると幸いです〜。
最後まで読んでいただきありがとうございました!!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?