【サーバーレスWebhook入門】独自ドメインのメールをCloudflare Workersで受信する

KaiMailのWebhookをCloudflare Workersで受信する方法を解説。サーバーレス構成で独自ドメインのメールをJSON化し、メンテナンス不要の自動化基盤を構築する


「メールが届いたら何かしたい」という要望は、意外と古くからある。注文確認メールを受け取って在庫を更新する、問い合わせを受け付けてチケットを作成する、請求書を受信して会計システムに取り込む。こうした自動化の壁は、メールサーバーの管理にある。メールを受信するためだけにサーバーを立てて、OSを更新し、セキュリティパッチを当て、SSL証明書を更新する。そこまでする価値があるのか、と思う人も多いだろう。

メールを受信して処理したいけどサーバーは管理したくない、という人向けに、Cloudflare Workersを使ったサーバーレス構成を紹介する。メンテナンスは不要、コードは数行、最初の10万件までは無料だ。

今回の記事は、メールを受信する側にKaiMailのWebhookを利用して、そのWebhookがCloudflare Workersに向けてリアルタイムでJSONを渡す、という仕組みを簡単に説明したいと思う。

サーバーレスでメールを受信するという発想

従来のメール自動化は、だいたい次のどちらかだった。

一つ目は、メールサーバーを自分で構築する方法。PostfixやDovecotをインストールして、メールが届いたらプログラムを呼び出す。確かに自由度は高いが、メールサーバーの運用は面倒だ。Spam対策、認証設定、証明書管理、OSアップデート。小さなプロジェクトには荷が重すぎる。

二つ目は、メールボックスを定期的にポーリングする方法。GmailやOutlookのAPIを5分おきに叩いて、新着メールがないか確認する。これはサーバーが不要だが、リアルタイム性がない。メールが届いてから実際に処理されるまでに数分の遅延が生じる。APIのレート制限にも気を遣う必要がある。

Webhookはこの両方の欠点を回避する。メールが届いた瞬間に、指定したURLへHTTP POSTで通知が飛んでくる。リアルタイムであり、メールサーバーの管理も不要だ。ただ、Webhookを受信するためのエンドポイントが必要になる(結局サーバーも立たなければいけないが、メールサーバーではないから難易度はちょっと低い)。より簡単な方法としてここでCloudflare Workersが役に立つ。

Cloudflare Workersは、Cloudflareのエッジネットワーク上でJavaScriptやTypeScriptを実行できるサーバーレスプラットフォームだ。従来のサーバーレス関数(AWS Lambdaなど)と比べて、コールドスタートがほとんどない。グローバルに分散された250以上のデータセンターで実行されるため、どこからアクセスしても遅延が少ない。

Cloudflare Workersの料金と制限

個人プロジェクトや小規模な自動化なら、無料プランで十分だ。2026年7月現在の料金体系は以下の通り。

項目 Freeプラン Paidプラン($5/月〜)
リクエスト数 10万件/日 1,000万件/月(超過分は100万件あたり$0.30)
CPU時間 10ms/呼び出し 3,000万ms/月(超過分は100万msあたり$0.02)
実行時間 10ms 最大5分(デフォルト30秒)
同時実行数 無制限 無制限

メールWebhookの処理は、JSONをパースして外部APIを叩く程度の軽い処理だ。1件あたり数msで終わる。1日10万件の無料枠があれば、大多数のユースケースはカバーできる。実際、個人のプロジェクトで1日10万件のメールを処理することはまずないだろう。

Paidプランに移行しても、月$5の基本料金で1,000万件のリクエストが含まれる。メール自動化の規模が大きくなっても、予算はそう膨れ上がらない。

WorkersでWebhookを受信する仕組み

Cloudflare Workersは、HTTPリクエストを受信するとfetchイベントハンドラを呼び出す。KaiMailのWebhookは、メールが届くと設定したURLへ署名付きJSONをPOSTする。これをWorkersで受信して処理する。

基本的な流れは以下の通り。

  1. KaiMailダッシュボードでカスタムドメインを登録
  2. メールボックスの転送先にWebhook URLを設定 ― これはCloudflare Workersのデプロイ後に発行されるURL(https://<your-worker>.workers.devなど)を、KaiMailダッシュボードの該当メールボックス設定に登録する
  3. Cloudflare WorkersでそのURLを待ち受け
  4. JSONペイロードを受信して処理

KaiMailのWebhookペイロードには、メールの送信元、件名、本文、添付ファイルなどが含まれる。署名検証も可能だが、まずは最小構成から始める。

最小構成のWorkersコード

最初に、署名検証なしの最小構成を動かしてみる。動作を確認してから、本番向けのセキュリティ対策を追加するのが順序だ。

// index.ts
export default {
  async fetch(request: Request): Promise<Response> {
    // POSTリクエストのみ受け付ける
    if (request.method !== 'POST') {
      return new Response('Method not allowed', { status: 405 });
    }

    // KaiMailからのJSONペイロードを取得
    const payload = await request.json();

    // 必要な情報を抽出
    const { from, to, subject, text, html } = payload;

    // ここで実際の処理を行う
    console.log(`受信: ${subject} (${from} → ${to})`);

    // 成功レスポンスを返す(KaiMailが再送しないように)
    return new Response('OK', { status: 200 });
  },
};

これだけだ。フレームワークのインストールも、サーバーの起動も不要。Cloudflareのダッシュボードからコードを貼り付けるか、Wrangler CLIを使ってデプロイすれば、数十秒で世界中のエッジで動作し始める。

Wrangler CLIでのデプロイは以下の通り。

# プロジェクトの初期化
npx wrangler init kaimail-webhook --template hello-world

# コードを書き換えてデプロイ
cd kaimail-webhook
npx wrangler deploy

デプロイ後、WorkersのURL(https://<your-worker>.workers.dev)をKaiMailのWebhook設定に登録すれば、すぐに動作する。

実用的な処理パターン3選

Webhookを受信して「OK」と返すだけでは、実用的な自動化にはならない。ここからは、実際に使える処理パターンを3つ紹介する。

パターン1:特定の送信元からのメールをフィルタリング

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (request.method !== 'POST') {
      return new Response('Method not allowed', { status: 405 });
    }

    const payload = await request.json();
    const { from, subject, text } = payload;

    // 特定のドメインからのメールのみ処理
    if (!from.endsWith('@example.com')) {
      return new Response('Ignored', { status: 200 });
    }

    // 件名に「請求書」が含まれる場合のみ処理
    if (subject.includes('請求書')) {
      await env.KV.put(`invoice:${Date.now()}`, JSON.stringify({
        from,
        subject,
        receivedAt: new Date().toISOString(),
      }));
    }

    return new Response('OK', { status: 200 });
  },
};

Cloudflare KVはキーバリューストアだ。請求書メールのメタデータを保存して、後からバッチ処理で一括ダウンロードする、といった使い方ができる。KVの無料枠は1日10万回の読み書きだ。

パターン2:外部APIに転送する

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (request.method !== 'POST') {
      return new Response('Method not allowed', { status: 405 });
    }

    const payload = await request.json();

    // Slackへ通知
    await fetch(env.SLACK_WEBHOOK_URL, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        text: `新着メール: ${payload.subject}\n送信元: ${payload.from}`,
      }),
    });

    return new Response('OK', { status: 200 });
  },
};

Workersから他のサービスへHTTPリクエストを送信するのは、追加料金なしで可能だ。Slack、Discord、LINE、自社APIなど、好きな宛先に転送できる。Workersからのサブリクエストは、メインのリクエストカウントには含まれない。

パターン3:署名検証を実装する

本番環境では、Webhookの署名検証を必ず実装すべきだ。KaiMailのWebhookには、ペイロードの改ざんを防ぐための署名が付いている。署名が正しいものでない場合、リクエストを無視する。

import { createHmac } from 'crypto';

async function verifySignature(
  payload: string,
  signature: string,
  secret: string
): Promise<boolean> {
  const expected = createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return signature === `sha256=${expected}`;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (request.method !== 'POST') {
      return new Response('Method not allowed', { status: 405 });
    }

    const body = await request.text();
    const signature = request.headers.get('X-KaiMail-Signature') || '';

    if (!await verifySignature(body, signature, env.KAIMAIL_WEBHOOK_SECRET)) {
      return new Response('Invalid signature', { status: 401 });
    }

    const payload = JSON.parse(body);
    console.log(`検証済み: ${payload.subject}`);

    return new Response('OK', { status: 200 });
  },
};

署名検証は「余分な作業」のように思えるが、これがないと誰でもWebhookエンドポイントを叩いて偽のメールデータを注入できる。セキュリティは後回しにしない方がいい。ただし、最小構成でまず動作確認してから、署名検証を追加するのが現実的だ。

バッグったら

実装してみると、いくつかの落とし穴がある。

Workersがタイムアウトする

Cloudflare Workersのデフォルト実行時間は30秒だ。KaiMailはWebhookが200 OKを返すまで再送を繰り返す。Webhook処理の中で重い外部API呼び出しをしていると、30秒を超えてタイムアウトする可能性がある。

対策は2つある。一つは、重い処理を非同期に切り出すこと。Cloudflare QueuesやDurable Objectsを使えば、Webhook受信と実処理を分離できる。もう一つは、実行時間の上限を引き上げること。Cron TriggerやQueue Consumerの場合は15分まで延長できるが、通常のHTTPリクエストは5分が上限だ。

実際のところ、メールWebhookの処理は数秒で終わるはずだ。もし30秒を超えるなら、設計を見直した方がいい。

メールにファイルを添付されてる場合がある。KaiMailからのJSONデータはファイルをダウンロードできるURLもついてるのだが、大きいファイルだとダウンロード時間もかかるだろう。ここも注意してほしい。

デバッグが面倒

ローカルで動かしているサーバーと違い、Cloudflare Workersのログはダッシュボードで確認する必要がある。console.logの出力は、Wrangler CLIのwrangler tailコマンドか、Cloudflareダッシュボードのログストリームで確認できる。

# リアルタイムでログを確認
npx wrangler tail

ローカルでの開発は、wrangler devコマンドで可能だ。ただし、KaiMailからのWebhookはローカル環境に届かない(KaiMailはインターネット上のURLしか叩けない)。ngrokなどのトンネルサービスを使うか、開発用のWebhookテスト機能を使って、実際のメールではなくテストペイロードを送信するのが現実的だ。

JSONパースに失敗する

まれに、メール本文に不正な文字列が含まれる場合、request.json()が失敗することがある。この場合は、生のテキストとして受け取ってから手動でパースするか、エラーハンドリングを入れる。

let payload;
try {
  payload = await request.json();
} catch (e) {
  return new Response('Invalid JSON', { status: 400 });
}

KaiMailのWebhookペイロードはUTF-8でエンコードされているが、メール本文そのものは元のエンコーディングを保持している場合がある。これはメールの仕様上の制約だ。通常のテキストメールであれば問題ないが、特定のエンコーディングを使ったメールでは文字化けが生じる可能性がある。

従来の構成と比較する

最後に、サーバーレス構成と従来の構成を比較してみる。

項目 サーバー構築(Postfix + Python) ローカル開発(Flask + ngrok) Cloudflare Workers
サーバー管理 必要(OS、パッチ、証明書) 不要(開発マシン上) 不要
インターネット公開 必要 ngrokが必要 自動(デプロイ後即公開)
スケーリング 手動(サーバー追加) 不可(1プロセス) 自動(エッジで分散)
コールドスタート なし(常時起動) なし ほぼなし(エッジ実行)
費用 VPS代($5〜/月) 無料(開発のみ) 無料〜$5/月
SSL証明書 手動更新 ngrokが管理 Cloudflareが管理
署名検証 自前実装 自前実装 自前実装(コードは同じ)

どの構成も一長一短がある。Postfixを自分で管理するのは面倒だが、最大の自由度がある。Flask + ngrokは開発や小規模運用には手軽だが、本番運用には向かない。Cloudflare Workersは本番運用に向いており、メンテナンスも少ないが、Cloudflareのプラットフォームにロックインされる。

個人的には、「メールを受信して何かしたい」というニーズの多くは、Cloudflare Workersで十分満たせると思っている。メールサーバーの管理に時間を取られるより、実際に解決したい問題に時間を使う方が建設的だ。

おさらいにすると、全体的像は異以下の感じ。

20260716-receive-domain-email-cloudflare-workers-ja

まとめ

KaiMailのWebhookをCloudflare Workersで受信することで、メンテナンス不要のメール自動化基盤が構築できる。最小構成は数行のコードで、デプロイは数十秒で完了する。最初の10万件は無料だ。

実装の順序としては、まず最小構成で動作確認をして、次に署名検証を追加し、最後に実用的な処理(外部API連携やKV保存)を実装するのがおすすめだ。一度基盤ができれば、新しい自動化フローの追加はWebhookエンドポイントに処理を追加するだけで済む。

サーバーレスのメリットは、インフラの管理から解放されることにある。メールを受信して処理したいだけなのに、メールサーバーの管理に週末を費やす必要はない。コードを書いてデプロイすれば、あとはCloudflareが面倒を見てくれる。

もちろん、プログラミング不要で既存サービスと連携したい場合は、Zapier連携ガイドも参考にしてほしい。Cloudflare Workersは開発者向けの柔軟な基盤だが、Zapierはノーコードで同様の連携が構築できる。

詳細なWebhookペイロードのスキーマや署名検証の仕様については、KaiMail Webhook連携ガイドを参照してください。Cloudflare Workersの詳細なAPIリファレンスは、Cloudflare Developersドキュメントを参照してください。