Cloudflare Workers の email() ハンドラーで、受信メールを処理します。カスタムロジックでメールルーティングをプログラムから扱えます。
Worker のエクスポートするハンドラーに、email ハンドラー関数を追加します。
export default {
async email(message, env, ctx): Promise<void> {
// Process incoming email
await message.forward("[email protected]");
},
} satisfies ExportedHandler<Env>;from workers import WorkerEntrypoint
class Default(WorkerEntrypoint):
async def email(self, message, env, ctx):
await message.forward("[email protected]")| パラメーター | 型 | 説明 |
|---|---|---|
message |
ForwardableEmailMessage |
受信メールメッセージ |
env |
object |
Worker の環境バインディング(KV、EMAIL など) |
ctx |
object |
waitUntil 関数を持つ実行コンテキスト |
message パラメーターで、受信メールにアクセスできます。
interface ForwardableEmailMessage {
readonly from: string; // Sender email address (envelope MAIL FROM)
readonly to: string; // Recipient email address (envelope RCPT TO)
readonly headers: Headers; // Email headers (Subject, Message-ID, etc.)
readonly raw: ReadableStream; // Raw MIME email content stream
readonly rawSize: number; // Size of raw email in bytes
readonly canBeForwarded: boolean; // Whether the message can be forwarded
// Actions
setReject(reason: string): void;
forward(rcptTo: string, headers?: Headers): Promise<EmailSendResult>;
reply(message: EmailMessage): Promise<EmailSendResult>;
}export default {
async email(message, env, ctx): Promise<void> {
// Access email metadata
console.log(`From: ${message.from}`);
console.log(`To: ${message.to}`);
console.log(`Size: ${message.rawSize} bytes`);
// Access headers
const subject = message.headers.get("subject");
const date = message.headers.get("date");
const messageId = message.headers.get("message-id");
console.log(`Subject: ${subject}`);
console.log(`Date: ${date}`);
console.log(`Message-ID: ${messageId}`);
},
};受信メールの MIME 構造を解析するには、postal-mime ↗ を使います。パーサーは、マルチパート境界、転送エンコーディング、文字セットを正しく処理します。
import PostalMime from "postal-mime";
export default {
async email(message, env, ctx): Promise<void> {
const email = await PostalMime.parse(message.raw);
console.log(`Subject: ${email.subject}`);
console.log(`Text: ${email.text}`);
console.log(`HTML: ${email.html}`);
},
};受信メールを、確認済みの転送先アドレスへ転送します。
export default {
async email(message, env, ctx): Promise<void> {
// Forward to a single address
await message.forward("[email protected]");
},
};export default {
async email(message, env, ctx): Promise<void> {
const recipient = message.to;
const subject = message.headers.get("subject") || "";
// Route based on recipient
if (recipient.includes("support@")) {
await message.forward("[email protected]");
} else if (recipient.includes("sales@")) {
await message.forward("[email protected]");
} else if (subject.toLowerCase().includes("urgent")) {
await message.forward("[email protected]");
} else {
// Default routing
await message.forward("[email protected]");
}
},
};export default {
async email(message, env, ctx): Promise<void> {
const subject = message.headers.get("subject") || "";
if (subject.toLowerCase().includes("security")) {
// Forward to multiple addresses for security issues
await Promise.all([
message.forward("[email protected]"),
message.forward("[email protected]"),
message.forward("[email protected]"),
]);
} else {
await message.forward("[email protected]");
}
},
};転送時にカスタムヘッダーを追加できます。forward() で追加できるのは X- プレフィックス付きのヘッダーだけです。それ以外のヘッダーは削除されます。
export default {
async email(message, env, ctx): Promise<void> {
// Create custom headers
const customHeaders = new Headers();
customHeaders.set("X-Processed-By", "Email-Worker");
customHeaders.set("X-Processing-Time", new Date().toISOString());
customHeaders.set("X-Original-Recipient", message.to);
customHeaders.set("X-Spam-Score", "0.1"); // Example spam score
// Forward with custom headers
await message.forward("[email protected]", customHeaders);
},
};message.reply() で自動返信を送れます。この方法で作った返信は、元のメッセージとスレッド化され、同じ SMTP セッションを通るため、元の Message-ID チェーンが保たれます。
Workers API 経由の返信は、次の要件を満たす必要があります。満たさない場合、reply() は例外を投げます。
- 受信メールに有効な DMARC 結果が必要です。
- 1 つの
EmailMessageイベントにつき、返信は 1 回だけです。 - 返信の受信者は、受信メールの送信者と一致する必要があります。
- 送信側の送信者ドメインは、メールを受信したドメインと一致する必要があります。
- 受信メールの
Referencesヘッダーが 100 件を超える場合、返信ループや悪用を防ぐため、返信は拒否されます。
返信のペイロードは、生の MIME 文字列から作る EmailMessage です。次の例では mimetext ↗ で MIME 本文を組み立てます。mimetext パッケージには nodejs_compat 互換性フラグが必要です。
import { EmailMessage } from "cloudflare:email";
import { createMimeMessage } from "mimetext";
export default {
async email(message, env, ctx): Promise<void> {
const subject = message.headers.get("subject") || "";
const messageId = message.headers.get("Message-ID");
const reply = createMimeMessage();
if (messageId) {
reply.setHeader("In-Reply-To", messageId);
reply.setHeader("References", messageId);
}
reply.setSender(message.to);
reply.setRecipient(message.from);
reply.setSubject(`Re: ${subject}`);
reply.addMessage({
contentType: "text/plain",
data: "Thank you for your message. We have received your email and will respond shortly.",
});
reply.addMessage({
contentType: "text/html",
data: "<h1>Thank you for your message</h1><p>We have received your email and will respond shortly.</p>",
});
await message.reply(
new EmailMessage(message.to, message.from, reply.asRaw()),
);
// Also forward to human team
await message.forward("[email protected]");
},
};import { EmailMessage } from "cloudflare:email";
import { createMimeMessage } from "mimetext";
export default {
async email(message, env, ctx): Promise<void> {
const sender = message.from;
const recipient = message.to;
const subject = message.headers.get("subject") || "";
const messageId = message.headers.get("Message-ID");
// Don't reply to automated emails
if (
sender.includes("noreply") ||
sender.includes("no-reply") ||
subject.toLowerCase().includes("automated")
) {
await message.forward("[email protected]");
return;
}
// Customized auto-reply based on recipient
let html = "";
if (recipient.includes("support@")) {
html = `
<h1>Support Request Received</h1>
<p>Thank you for contacting support. Your request has been assigned ticket #${Date.now()}.</p>
<p>Expected response time: 2-4 hours during business hours.</p>
`;
} else if (recipient.includes("sales@")) {
html = `
<h1>Sales Inquiry Received</h1>
<p>Thank you for your interest in our products.</p>
<p>A sales representative will contact you within 24 hours.</p>
`;
} else {
html = `
<h1>Message Received</h1>
<p>Thank you for your message. We will respond within 2 business days.</p>
`;
}
const reply = createMimeMessage();
if (messageId) {
reply.setHeader("In-Reply-To", messageId);
reply.setHeader("References", messageId);
}
reply.setSender(recipient);
reply.setRecipient(sender);
reply.setSubject(`Re: ${subject}`);
reply.addMessage({
contentType: "text/plain",
data: html.replace(/<[^>]*>/g, ""),
});
reply.addMessage({ contentType: "text/html", data: html });
await message.reply(new EmailMessage(recipient, sender, reply.asRaw()));
// Forward to appropriate team
await message.forward("[email protected]");
},
};永続的な SMTP エラーでメールを拒否します。
export default {
async email(message, env, ctx): Promise<void> {
const sender = message.from;
// Block specific senders
const blockedDomains = ["spam.com", "unwanted.net"];
const senderDomain = sender.split("@")[1];
if (blockedDomains.includes(senderDomain)) {
message.setReject("Sender domain not allowed");
return;
}
// Continue processing
await message.forward("[email protected]");
},
};export default {
async email(message, env, ctx): Promise<void> {
const subject = message.headers.get("subject") || "";
// Reject based on subject content
const spamKeywords = ["buy now", "limited time", "act fast", "urgent"];
const containsSpam = spamKeywords.some((keyword) =>
subject.toLowerCase().includes(keyword),
);
if (containsSpam) {
message.setReject("Message appears to be spam");
return;
}
// Check message size
if (message.rawSize > 25 * 1024 * 1024) {
// 25 MiB limit (inbound message size)
message.setReject("Message too large");
return;
}
// Continue processing
await message.forward("[email protected]");
},
};メール処理では、エラーを適切に扱います。
export default {
async email(message, env, ctx): Promise<void> {
try {
// Main email processing logic
await processEmail(message, env);
} catch (error) {
console.error("Email processing failed:", error);
// Log error for monitoring
if (env.ERROR_LOGS) {
await env.ERROR_LOGS.put(
`error-${Date.now()}`,
JSON.stringify({
error: error.message,
stack: error.stack,
from: message.from,
to: message.to,
timestamp: new Date().toISOString(),
}),
);
}
// Fallback: forward to admin
try {
await message.forward("[email protected]");
} catch (fallbackError) {
console.error("Fallback forwarding failed:", fallbackError);
// Last resort: reject the email
message.setReject("Internal processing error");
}
}
},
};
async function processEmail(message, env) {
// Your main email processing logic here
const recipient = message.to;
if (recipient.includes("support@")) {
await message.forward("[email protected]");
} else if (recipient.includes("sales@")) {
await message.forward("[email protected]");
} else {
await message.forward("[email protected]");
}
}- ローカルでテストする: メールルーティングの開発
- Email Routing REST API で、ルールとアドレスをプログラムから管理します
- メールルーティングの設定 を行います
- 高度なメール処理は メールルーティングの例 を参照してください
- Workers による スパムフィルタリング を確認します