# Descadastro de um clique (RFC 8058): como implementar

> Como implementar o descadastro de um clique com List-Unsubscribe e List-Unsubscribe-Post (RFC 8058), e como evitar descadastros falsos por scanners.

Source: https://pidgely.com/pt-br/blog/descadastro-um-clique-rfc-8058/ · Published: 2026-10-12 · Pidgely

O descadastro de um clique, definido na RFC 8058, permite que o próprio cliente de email (Gmail, Yahoo, Apple Mail e outros) descadastre o usuário com um botão, enviando um POST HTTPS para uma URL que você informa no header `List-Unsubscribe`, acompanhado do header `List-Unsubscribe-Post: List-Unsubscribe=One-Click`. O seu servidor recebe o POST e remove o endereço sem pedir login nem confirmação. Desde 2024, Google e Yahoo exigem esse mecanismo em mensagens comerciais de remetentes em massa, com o pedido honrado em até dois dias.

## Os dois headers

```text
List-Unsubscribe: <https://example.com.br/descadastro/eyJ1IjoiOWYyYyJ9.c2ln>,
 <mailto:descadastro@example.com.br?subject=9f2c>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
```

- **`List-Unsubscribe`** (RFC 2369) lista uma ou mais URIs entre `< >`. Para o um clique, pelo menos uma precisa ser `https:`. O `mailto:` é opcional e funciona como alternativa para clientes que não suportam POST.
- **`List-Unsubscribe-Post`** (RFC 8058) tem sempre o valor exato `List-Unsubscribe=One-Click`. Ele sinaliza que a URL HTTPS aceita o POST de um clique.

A RFC 8058 também exige que a mensagem tenha uma **assinatura DKIM válida que cubra os dois headers**. Sem isso, um intermediário poderia trocar a URL. Confira o `h=` do `DKIM-Signature`:

```text
DKIM-Signature: v=1; a=rsa-sha256; d=example.com.br; s=pid2026;
 h=from:to:subject:date:message-id:list-unsubscribe:list-unsubscribe-post; ...
```

## O que o cliente de email envia

Quando o usuário clica em "Cancelar inscrição" na interface do Gmail, por exemplo, o provedor faz algo assim:

```http
POST /descadastro/eyJ1IjoiOWYyYyJ9.c2ln HTTP/1.1
Host: example.com.br
Content-Type: application/x-www-form-urlencoded
Content-Length: 26

List-Unsubscribe=One-Click
```

Repare: **sem cookies, sem sessão, sem redirecionamento para tela de confirmação**. A RFC pede que a URL contenha todas as informações necessárias para identificar o destinatário e a lista, e que o POST não dependa de nada além disso.

## Implementação passo a passo

### 1. Gere um token opaco e assinado

A URL precisa identificar quem e de qual lista descadastrar, sem expor o email em texto puro e sem permitir que alguém descadastre outra pessoa trocando um ID. Uma forma simples é um payload assinado com HMAC:

```python
import hmac, hashlib, base64, json

SEGREDO = b"troque-por-um-segredo-forte"

def gerar_token(contato_id: str, stream: str) -> str:
    payload = base64.urlsafe_b64encode(
        json.dumps({"c": contato_id, "s": stream}).encode()
    ).rstrip(b"=")
    assinatura = base64.urlsafe_b64encode(
        hmac.new(SEGREDO, payload, hashlib.sha256).digest()
    ).rstrip(b"=")
    return f"{payload.decode()}.{assinatura.decode()}"
```

Na verificação, use comparação em tempo constante (`hmac.compare_digest`).

### 2. Separe POST de GET

- **POST** com corpo `List-Unsubscribe=One-Click`: descadastra imediatamente e responde `200` (ou `202`). Nada de HTML de confirmação.
- **GET**: mostra uma página com um botão "Confirmar descadastro" que, por sua vez, faz um POST. **Nunca** descadastre em GET.

```python
@app.post("/descadastro/<token>")
def descadastro_post(token):
    dados = validar_token(token)          # 400 se inválido
    suprimir(dados["c"], stream=dados["s"], origem="one-click")
    return "", 200

@app.get("/descadastro/<token>")
def descadastro_get(token):
    validar_token(token)
    return render("confirmar_descadastro.html", token=token)
```

### 3. Torne a operação idempotente

O mesmo POST pode chegar várias vezes (retentativas do provedor, clique duplo). Descadastrar alguém que já está descadastrado deve retornar sucesso, não erro.

### 4. Defina o escopo

Descadastrar da newsletter não deveria bloquear o envio de nota fiscal ou do código de login. Inclua o stream ou a categoria no token e aplique a supressão apenas nele. Isso também ajuda a cumprir a LGPD, que exige respeitar a revogação do consentimento para a finalidade específica.

### 5. Mantenha o link visível no corpo

O header não substitui o link no rodapé. Usuários que não veem o botão do cliente de email precisam de outra forma de sair, e as diretrizes do Google e do Yahoo continuam pedindo um link claro na mensagem.

## A armadilha dos scanners de link

Gateways de segurança corporativos, antivírus e pré-visualizadores de URL abrem os links das mensagens para verificar se são maliciosos. Se o seu link de descadastro funciona com GET, esses robôs descadastram pessoas que nunca pediram. Sintomas típicos:

- Descadastros que acontecem segundos depois da entrega.
- Picos de descadastro concentrados em domínios corporativos.
- Vários links da mesma mensagem acessados em sequência, do mesmo IP.

A defesa é a da seção anterior: **efeito colateral só em POST**. Scanners em geral fazem GET; o POST de um clique vem do provedor de email, por ação do usuário. Se você precisar de um link no corpo que funcione sem JavaScript, faça o GET exibir um formulário com botão.

## Outras armadilhas

- **Redirecionar o POST** para outra URL. Alguns clientes não seguem redirecionamentos em POST; responda diretamente.
- **Exigir login** para descadastrar. Viola a RFC e gera reclamação de spam, que custa muito mais caro.
- **Responder com erro em token expirado.** Mensagens antigas continuam na caixa do usuário. Tokens de descadastro não devem expirar, ou devem durar muito tempo.
- **Header quebrado.** URLs longas sem dobra correta (folding) ou sem `< >` fazem o cliente ignorar o header.
- **DKIM sem os headers no `h=`.** O botão pode não aparecer ou o provedor pode ignorar o um clique.
- **Processar em lote uma vez por semana.** O prazo é de dois dias; o ideal é na hora.

## Como testar

1. Envie para uma conta do Gmail e verifique se o link "Cancelar inscrição" aparece ao lado do remetente (ele não aparece para todos os remetentes; depende também de reputação).
2. Simule o POST:

```bash
curl -i -X POST \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data "List-Unsubscribe=One-Click" \
  "https://example.com.br/descadastro/eyJ1IjoiOWYyYyJ9.c2ln"
```

3. Faça um GET na mesma URL e confirme que o contato **continua** inscrito.
4. Repita o POST e confirme que a resposta continua `200`.

## Checklist

- [ ] `List-Unsubscribe` com URL HTTPS (e `mailto:` opcional).
- [ ] `List-Unsubscribe-Post: List-Unsubscribe=One-Click`.
- [ ] Os dois headers no `h=` da assinatura DKIM.
- [ ] Token assinado, sem email em texto puro, sem expiração curta.
- [ ] POST descadastra na hora, sem redirecionamento, de forma idempotente.
- [ ] GET nunca descadastra.
- [ ] Supressão por stream ou categoria.
- [ ] Link visível no corpo da mensagem.

## Na Pidgely

A Pidgely adiciona os headers da RFC 8058 nos envios comerciais, assina com DKIM cobrindo esses headers, hospeda o endpoint de POST e registra o descadastro na lista de supressão do stream certo, disparando um webhook para a sua aplicação sincronizar o status do contato.

## Perguntas frequentes

### O que é o descadastro de um clique da RFC 8058?

É um mecanismo em que o cliente de email envia um POST HTTPS para a URL do header List-Unsubscribe, com o corpo List-Unsubscribe=One-Click, e o remetente descadastra o usuário sem pedir confirmação nem login.

### Só o header List-Unsubscribe com mailto basta?

Não para o requisito de um clique do Gmail e do Yahoo. É preciso uma URL HTTPS no List-Unsubscribe e o header List-Unsubscribe-Post: List-Unsubscribe=One-Click, ambos cobertos pela assinatura DKIM.

### Por que o descadastro deve aceitar só POST?

Porque scanners de segurança e pré-visualizadores de link fazem GET em URLs das mensagens. Se um GET descadastrar, usuários serão removidos sem ter pedido.

### Em quanto tempo preciso processar o descadastro?

Google e Yahoo pedem que o pedido seja honrado em até dois dias. O ideal é descadastrar na hora, de forma síncrona, no próprio endpoint.

### Mensagens transacionais precisam desses headers?

A exigência vale para mensagens comerciais e promocionais. Confirmação de pedido ou redefinição de senha não precisam, e geralmente não devem ter o descadastro do marketing.

