Pular para o conteúdo principal

MailHog vs Mailpit: Testando E-mails de Verificação e Redefinição de Senha com Casdoor

· Leitura de 10 min
Yang Luo
Casdoor Maintainer

MailHog e Mailpit são ambos servidores SMTP falsos para desenvolvimento: eles aceitam toda mensagem, mantêm-na e mostram-na em uma interface web em vez de entregá-la. Ambos usam as mesmas portas padrão, SMTP na 1025 e a interface web e API na 8025, então o endereço SMTP é smtp://127.0.0.1:1025 e a caixa de entrada está em http://127.0.0.1:8025 para qualquer um deles. A diferença é a manutenção: a última versão do MailHog, v1.0.1, foi lançada em agosto de 2020, enquanto o Mailpit lançou a v1.33.0 em 31 de outubro de 2026. Para um novo projeto, use o Mailpit. O Casdoor envia para ambos através de um provedor SMTP simples, permitindo testar códigos de cadastro, e-mails de redefinição de senha e links mágicos sem uma conta de e-mail real.

MailHog vs Mailpit em resumo​

MailHogMailpit
Última versãov1.0.1, 2020-08-11v1.33.0, 2026-10-31
Último commit no branch padrão2022-08-022026-10-31
LicençaMITMIT
Porta SMTP padrão10251025
Porta padrão da interface web e API80258025
Imagem Dockermailhog/mailhog, somente amd64axllent/mailpit, amd64, arm64 e 386
API HTTPv1 e v2: listar, obter, excluir, pesquisar por from, to ou containingv1: listar, obter, excluir, pesquisar com filtros como to: e subject:, atalho para mensagem latest
Armazenamento padrãoEm memóriaBanco de dados SQLite temporário, excluído na saída
Armazenamento persistenteMongoDB ou MaildirArquivo SQLite (MP_DATABASE) ou rqlite
Limite de mensagensNenhum documentadoMantém as 500 mais recentes por padrão (MP_MAX_MESSAGES)
Verificação HTML, verificação de links, verificação de spamNãoSim
Webhook em novo e-mailNãoSim (MP_WEBHOOK_URL)
SMTP TLSNão oferecidoSTARTTLS opcional ou SSL/TLS
Injeção de falhasJimChaos
Liberar uma mensagem para um servidor SMTP realSimSim
Servidor POP3NãoSim, opcional
Autenticação básica para a interface webSimSim

As contagens do Docker Hub mostram que muitas pessoas ainda baixam o MailHog. Isso reflete tutoriais antigos e arquivos de CI, não atividade: uma issue aberta no repositório do MailHog intitulada "THIS PROJECT IS DEAD!" tem uma resposta de 2023 do autor dizendo que ele não tem mais tempo para mantê-lo. O README do Mailpit se descreve como inspirado pelo MailHog e aponta para esse mesmo comentário.

A arquitetura da imagem importa mais do que parece. Em um laptop Apple Silicon ou em um runner ARM de CI, mailhog/mailhog roda apenas sob emulação, enquanto axllent/mailpit tem uma imagem nativa arm64.

Execute qualquer um deles com Docker​

MailHog:

docker run -d --name mailhog -p 1025:1025 -p 8025:8025 mailhog/mailhog

Mailpit:

docker run -d --name mailpit -p 1025:1025 -p 8025:8025 axllent/mailpit

O Mailpit mantém os e-mails em um banco de dados temporário que desaparece quando o container é parado. Para mantê-lo entre reinicializações, monte um volume e defina MP_DATABASE:

docker run -d --name mailpit \
-v "$PWD/mailpit-data:/data" \
-e MP_DATABASE=/data/mailpit.db \
-p 1025:1025 -p 8025:8025 \
axllent/mailpit

Sem Docker, tanto brew install mailhog quanto brew install mailpit funcionam no macOS, e o Mailpit oferece binários estáticos para Windows, Linux e macOS em sua página de releases.

No GitHub Actions, execute-o como um container de serviço ao lado do seu job de teste:

services:
mailpit:
image: axllent/mailpit
ports:
- 1025:1025
- 8025:8025

Configure o Casdoor para usar o MailHog ou Mailpit​

O Casdoor não tem um tipo de provedor separado para MailHog ou Mailpit, e não precisa de um. O tipo de e-mail Default envia via SMTP, e ambas as ferramentas são servidores SMTP.

  1. Como administrador global, adicione um provedor na organização admin ou built-in.
  2. Defina Category como Email e Type como Default.
  3. Defina Host para onde a ferramenta é executada e Port para 1025.
  4. Deixe Username e Password vazios.
  5. Clique em Test SMTP Connection, depois em Send Testing Email, e verifique a interface web na porta 8025.

As páginas passo a passo com capturas de tela são Test email with MailHog e Test email with Mailpit.

Alguns detalhes decidem se isso funciona na primeira tentativa:

Deixe o modo SSL em Auto ou defina-o para Disable. No modo Auto, o Casdoor usa TLS implícito apenas na porta 465 e faz upgrade com STARTTLS apenas se o servidor oferecer isso. Nem o MailHog nem um Mailpit padrão oferecem STARTTLS na porta 1025, então a conexão permanece sem criptografia. Não escolha Enable: isso força TLS implícito, e o handshake falha contra um servidor de texto plano. A tabela de modos SSL tem o comportamento completo.

Deixe as credenciais vazias. O Casdoor faz login no servidor SMTP apenas quando Username está definido. Se você preenchê-lo, a autenticação PLAIN do Go recusa enviar a senha sobre uma conexão não criptografada, a menos que o host seja localhost, então um MailHog em 192.168.x.x falha com um erro de autenticação, mesmo que o MailHog aceitaria qualquer login. O Mailpit, por padrão, não solicita autenticação.

Use o host correto. 127.0.0.1 funciona apenas quando o Casdoor e o coletor de e-mails rodam na mesma máquina. Se ambos rodarem no Docker Compose, use o nome do serviço, como mailpit, como host.

Use um provedor global. Um provedor pertencente a uma organização comum só pode acessar endereços da Internet, então não pode conectar-se a 127.0.0.1 ou a um IP privado. Provedores em admin ou built-in não têm restrições. Veja Which addresses a provider can reach.

Defina origin para os links. Os códigos de verificação funcionam imediatamente. Links de redefinição de senha e magic links precisam que o Casdoor saiba sua URL pública, através de origin em conf/app.conf ou de uma requisição em um host loopback. Caso contrário, o link de redefinição será omitido do e-mail e os magic links falharão. Veja Templates.

Finalmente, adicione o provedor à sua aplicação em Add providers to an application para que o cadastro e a redefinição de senha realmente o utilizem.

Leia o código de verificação em um teste automatizado​

Os códigos de verificação do Casdoor têm seis dígitos, e o template padrão diz "Here is your code: " seguido pelo código. Um teste end-to-end cadastra um usuário, pede ao Casdoor para enviar o código, e depois lê-o pela API do coletor de e-mails.

Com o Mailpit, pesquise pelo destinatário e abra a correspondência mais recente. GET /api/v1/search retorna as correspondências da mais nova para a mais antiga, e GET /api/v1/message/{ID} retorna os corpos Text e HTML analisados:

import { request } from "@playwright/test";

const MAILPIT = process.env.MAILPIT_URL ?? "http://127.0.0.1:8025";

export async function latestCode(to: string): Promise<string> {
const api = await request.newContext({ baseURL: MAILPIT });
for (let i = 0; i < 20; i++) {
const res = await api.get("/api/v1/search", {
params: { query: `to:"${to}"`, limit: "1" },
});
const { messages } = await res.json();
if (messages.length > 0) {
const msg = await (await api.get(`/api/v1/message/${messages[0].ID}`)).json();
const match = (msg.Text || msg.HTML).match(/code: (\d{6})/);
if (match) return match[1];
}
await new Promise((r) => setTimeout(r, 500));
}
throw new Error(`no verification email for ${to}`);
}

Se apenas um teste enviar e-mails por vez, GET /api/v1/message/latest ignora a etapa de busca. Antes de cada teste, DELETE /api/v1/messages sem corpo limpa a caixa de entrada.

Com o MailHog, a chamada mais próxima é GET /api/v2/search?kind=to&query=<address>. As mensagens são retornadas da mais recente para a mais antiga em items, e o corpo está em items[0].Content.Body:

const res = await api.get("/api/v2/search", {
params: { kind: "to", query: to, limit: "1" },
});
const { items } = await res.json();
const body: string = items[0]?.Content.Body ?? "";

Content.Body é o corpo MIME bruto. O Casdoor envia em quoted-printable, então = se torna =3D e linhas longas recebem quebras suaves terminando em =. Decodifique-o primeiro ou mantenha o código no início de um modelo curto. Limpe o MailHog com DELETE /api/v1/messages.

Migrando do MailHog para o Mailpit​

Como as portas correspondem, a maioria das migrações é uma alteração de uma linha no docker-compose.yml: substitua mailhog/mailhog por axllent/mailpit. O provedor do Casdoor mantém o mesmo host e porta. O que muda:

  • Configurações. O MailHog lê variáveis MH_*; o Mailpit lê MP_*. Se você usou MH_STORAGE=maildir ou MongoDB, mude para MP_DATABASE.
  • Auxiliares de teste. O Mailpit não possui endpoints /api/v2. Reescreva chamadas para /api/v2/messages e /api/v2/search para /api/v1/messages e /api/v1/search, e leia messages[].ID além de Text ou HTML em vez de items[].Content.Body. A decodificação quoted-printable desaparece.
  • Sintaxe de busca. kind=to&query=a@example.com se torna query=to:"a@example.com".
  • Injeção de falhas. Jim não tem flags no Mailpit; use Chaos.
  • Autenticação. Se um aplicativo insistir em fazer login via SMTP simples, inicie o Mailpit com MP_SMTP_AUTH_ACCEPT_ANY=1 e MP_SMTP_AUTH_ALLOW_INSECURE=1. Para o Casdoor, deixar as credenciais vazias é mais simples.
  • Retenção. O Mailpit exclui para manter apenas as 500 mensagens mais recentes por padrão. Aumente MP_MAX_MESSAGES se uma execução de testes longa precisar de mais.

Indo para produção​

MailHog e Mailpit nunca entregam e-mails a caixas de entrada reais, a menos que você libere as mensagens manualmente, portanto, a produção precisa de um provedor real. No Casdoor, você troca o provedor, não os modelos. Os modelos de e-mail, o placeholder de código %s e o bloco <reset-link> permanecem os mesmos:

Uma configuração comum mantém o Mailpit no desenvolvimento e CI e o provedor real na produção. Cada ambiente aponta sua aplicação para um provedor diferente, para que o fluxo de cadastro que você testa seja o que será enviado.

Referências​