A maioria dos tutoriais sobre Cloudflare Email Routing mostra como encaminhar contato@seudominio.com para um Gmail. Isso resolve o caso de uso mais simples — mas esconde a parte mais interessante do serviço. Quando você roteia um email para um Worker em vez de um endereço de destino, o email vira dado: você lê o remetente, o assunto, os headers, o corpo completo, e decide o que fazer com tudo isso dentro de uma função JavaScript rodando no edge. Isso muda bastante o que é viável construir sem infraestrutura de email própria.
O handler de email e o que você recebe
A estrutura básica de um Email Worker usa um export nomeado email dentro do objeto padrão:
export default { async email(message, env, ctx) { // message.from — endereço do remetente // message.to — endereço de destino no seu domínio // message.headers — objeto Headers com todos os cabeçalhos RFC 2822 // message.raw — ReadableStream com a mensagem completa } }
message.from e message.to são strings com os endereços. message.headers é um objeto Headers padrão da Web API — você acessa message.headers.get('subject') ou message.headers.get('x-mailer') da mesma forma que faria num fetch handler. message.raw é um ReadableStream com a mensagem RFC 2822 inteira, incluindo headers e body, com suporte a mensagens de até 25MB.
As ações disponíveis são quatro: message.forward(address) para encaminhar para um endereço verificado, message.reply(response) para responder, message.setReject(reason) para rejeitar a mensagem com uma mensagem de erro, ou simplesmente retornar sem chamar nada — o que descarta a mensagem silenciosamente. Você pode combinar: filtrar por remetente, encaminhar alguns, rejeitar outros, e processar o restante.
O gap do parsing de MIME
Aqui está o ponto que a documentação menciona de forma discreta: não há parser de MIME embutido. message.raw te dá o stream cru. Se você quer extrair o assunto com encoding UTF-8, o corpo em texto plano, o HTML alternativo, ou os anexos, você precisa parsear o MIME você mesmo — ou usar uma biblioteca.
A postal-mime funciona bem no ambiente Workers. O padrão é consumir o stream, converter para ArrayBuffer, e passar para o parser:
import PostalMime from 'postal-mime'; const raw = await new Response(message.raw).arrayBuffer(); const parsed = await new PostalMime().parse(raw); // parsed.subject, parsed.text, parsed.html, parsed.attachments
parsed.attachments é um array de objetos com filename, mimeType, e content (ArrayBuffer). Você pode gravar o conteúdo num bucket R2, extrair metadados de uma NF-e em XML, ou passar um PDF para uma API de extração. O Workers runtime suporta isso sem nenhuma configuração adicional além do import da biblioteca.
O custo de consumir o stream inteiro é memória. Para mensagens com anexos grandes próximos do limite de 25MB, você está carregando tudo em memória no Worker. Para a maioria dos casos isso não é problema, mas em pipelines de alto volume vale monitorar.
Padrões que fazem sentido construir aqui
Criação automática de tickets de suporte. O email chega, você extrai remetente, assunto e corpo, monta um payload e faz POST para a API do Linear, Zendesk, Notion, ou qualquer ferramenta que sua equipe usa. O ticket já aparece com o contexto completo sem ninguém precisar copiar e colar. Para times que recebem solicitações por email mas trabalham em ferramentas de issue tracking, isso elimina uma etapa manual constante.
Captura de notas fiscais e documentos fiscais. Você cria um endereço dedicado — nfe@seudominio.com — e qualquer fornecedor que manda NF-e por email tem o anexo XML ou PDF processado automaticamente: metadados extraídos, arquivo salvo no R2, registro criado no D1. O Worker faz o que um humano faria, mas sem precisar abrir o email.
Filtragem antes de encaminhar. Com catch-all ativo, *@seudominio.com captura spam enviado para endereços aleatórios do seu domínio. Um Worker pode checar o remetente contra uma lista de domínios conhecidos como problemáticos armazenada no KV, verificar se o subject contém padrões típicos de spam, e chamar message.setReject('spam detectado') antes de encaminhar para seu inbox. Não é um filtro de spam completo, mas reduz ruído sem custo adicional.
Alertas e notificações reformatadas. Ferramentas de monitoramento — Grafana, PagerDuty, ferramentas de CI — mandam alertas por email com formatos que nem sempre são legíveis num telefone. Um Worker intercepta esses emails, extrai as informações relevantes do corpo, e posta uma mensagem formatada num canal do Slack ou Discord via webhook. O email original pode ser descartado ou encaminhado como arquivo.
O que não funciona como você imagina
message.reply() existe e funciona, mas a resposta sai de noreply@cloudflare.com. Se você quer que o destinatário receba uma resposta automática que pareça vir de suporte@seudominio.com, o Worker precisa chamar um serviço de SMTP outbound — Resend, Mailgun, SES — passando os headers necessários. O Email Routing não tem acesso ao fluxo de saída do seu domínio.
Se o Worker lança uma exceção não capturada, o email é rejeitado com erro 500. Não há retry automático, não há fila de dead-letter. Qualquer lógica que possa falhar precisa de try/catch com fallback explícito — normalmente um message.forward() para um endereço de triagem manual quando algo dá errado no processamento. Descobrir esse comportamento em produção, com email de cliente rejeitado, é uma experiência desagradável.
Onde faz sentido ir além do forwarding
Se o Email Routing para você é só um alias para o Gmail, você nunca vai tocar nos Workers e não precisa. Mas se seu time já usa Workers para outros fins — APIs, cron jobs, integrações — o Email Worker se encaixa na mesma infraestrutura com os mesmos bindings de KV, D1, R2 e serviços externos. Você não está adicionando uma peça nova ao stack; está adicionando um tipo de trigger a um ambiente que já existe.
O momento certo de sair do simples forwarding é quando você percebe que alguém do time abre emails para copiar informação para outro sistema mais de uma ou duas vezes por semana. Se o padrão é repetível e o dado está no email, o Worker resolve em menos tempo do que a tarefa manual vai consumir num mês.
Leia também
- Cloudflare Durable Objects: estado consistente no edge — o que realmente muda
- Cloudflare Workers vs Pages: a diferença que importa antes de você escolher
- Pages Functions: quando usar em vez de Workers puros
- Cloudflare KV: o que globalmente distribuído significa quando você precisa escrever
- Cloudflare Email Routing: receber email no seu domínio — e o que não vem junto
- As limitações do Cloudflare Email Routing que o tutorial não menciona