# Baja con un clic (RFC 8058): cómo implementarla bien

> Cómo implementar la baja con un clic según RFC 8058: headers List-Unsubscribe y List-Unsubscribe-Post, endpoint POST, firma DKIM y la trampa de los escáneres.

Source: https://pidgely.com/es/blog/baja-con-un-clic-rfc-8058/ · Published: 2026-10-12 · Pidgely

La baja con un clic definida en **RFC 8058** permite que Gmail, Yahoo y otros clientes muestren un botón "Cancelar suscripción" que da de baja al usuario sin abrir ninguna página. Se implementa con dos headers, `List-Unsubscribe` (con una URL HTTPS) y `List-Unsubscribe-Post: List-Unsubscribe=One-Click`, ambos firmados con DKIM, y un endpoint que procesa la baja al recibir una petición **POST**. Desde febrero de 2024 es obligatoria para el correo comercial de los remitentes masivos según las directrices de Google y Yahoo.

## El problema que resuelve

El header `List-Unsubscribe` existe desde RFC 2369 (1998). Permitía indicar una dirección `mailto:` o una URL para darse de baja, pero no definía qué debía pasar al abrir esa URL. Muchos remitentes la llevaban a una página con formularios, inicios de sesión o encuestas, así que los clientes de correo no podían ofrecer una baja automática fiable.

RFC 8058 añade una señal explícita: si el mensaje incluye `List-Unsubscribe-Post`, el cliente puede hacer un **POST** a la URL y el remitente se compromete a dar de baja sin más pasos. El resultado para el usuario es un botón nativo en la interfaz. Para ti, menos quejas: quien quiere dejar de recibir tus correos tiene una alternativa más cómoda que "Marcar como spam".

## Los headers

```text
List-Unsubscribe: <https://example.com/unsubscribe/one-click?t=eyJsIjoibmV3cyIsInIiOiI4ZjJhIn0.c2ln>,
 <mailto:unsubscribe+8f2a@example.com?subject=unsubscribe>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
```

Requisitos de RFC 8058:

- **Al menos una URI HTTPS** en `List-Unsubscribe`. El `mailto:` es opcional, pero útil para clientes que no soportan el POST.
- **`List-Unsubscribe-Post`** con el valor exacto `List-Unsubscribe=One-Click`.
- **Firma DKIM válida que cubra ambos headers** (deben aparecer en la etiqueta `h=` del `DKIM-Signature`). Sin esa firma, un intermediario podría inyectar una URL maliciosa y el cliente no debería ofrecer el botón.
- **La URL debe identificar al destinatario y la lista** por sí sola, normalmente mediante un token opaco.

## Lo que hace el cliente

Cuando el usuario pulsa el botón, el cliente de correo (o su backend) envía:

```http
POST /unsubscribe/one-click?t=eyJsIjoibmV3cyIsInIiOiI4ZjJhIn0.c2ln HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 26

List-Unsubscribe=One-Click
```

Ese POST no lleva cookies ni credenciales del usuario. Tu servidor debe:

1. Validar el token.
2. Registrar la baja.
3. Responder con un `2xx`.

Nada más. No hay confirmación, ni redirección a una página, ni encuesta.

## Implementación del endpoint

Un ejemplo en Node.js con Express:

```js
import express from "express";
import crypto from "node:crypto";

const app = express();
const SECRET = process.env.UNSUB_SECRET;

function verifyToken(token) {
  const [payload, sig] = token.split(".");
  if (!payload || !sig) return null;
  const expected = crypto
    .createHmac("sha256", SECRET)
    .update(payload)
    .digest("base64url");
  const a = Buffer.from(sig);
  const b = Buffer.from(expected);
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) return null;
  return JSON.parse(Buffer.from(payload, "base64url").toString());
}

// POST: ejecuta la baja (RFC 8058)
app.post(
  "/unsubscribe/one-click",
  express.urlencoded({ extended: false }),
  async (req, res) => {
    const data = verifyToken(String(req.query.t || ""));
    if (!data) return res.status(400).end();
    await suppress({ list: data.l, recipient: data.r, source: "one-click" });
    res.status(200).end();
  }
);

// GET: nunca da de baja, solo muestra una página con un botón
app.get("/unsubscribe/one-click", (req, res) => {
  res.send(renderConfirmationPage(req.query.t));
});
```

Detalles importantes:

- **Firma el token con HMAC** y compara en tiempo constante. Un token predecible (como un ID secuencial) permitiría a cualquiera dar de baja a otros.
- **Incluye la lista en el token**, para dar de baja solo de esa lista y no de todo, si así lo ofreces. Lo que no debes hacer es dar de baja de menos de lo que el usuario espera.
- **Haz la operación idempotente.** El cliente puede repetir el POST. Dar de baja dos veces no debe fallar.
- **Responde rápido.** Si la escritura es lenta, encola la baja, pero confírmala en tu sistema de supresión lo antes posible.
- **Acepta también `multipart/form-data`**, que RFC 8058 permite como alternativa.

## La trampa de los escáneres de enlaces

Los filtros corporativos, antivirus y algunos clientes de correo **visitan los enlaces de los mensajes** para analizarlos antes de que el usuario los vea. Si tu URL de baja ejecuta la baja con un simple GET, esos escáneres darán de baja a usuarios que nunca lo pidieron. Es un problema conocido y difícil de detectar: verás bajas "fantasma" concentradas en dominios empresariales, a menudo segundos después de la entrega.

Por eso RFC 8058 usa POST. La regla es:

- **GET → página de confirmación** con un botón que envía un POST (formulario simple, sin JavaScript obligatorio).
- **POST → baja efectiva.**

El enlace de baja visible en el cuerpo del mensaje puede apuntar a la misma URL: el usuario llega por GET, ve la página y confirma con un botón. Así cumples con el enlace visible que piden Google y Yahoo sin exponerte a los escáneres.

## Y el `mailto:`

Si incluyes `mailto:`, debes procesar los correos que lleguen a esa dirección: extraer el token del local-part (`unsubscribe+8f2a@...`), validarlo y aplicar la baja. Ignorar ese buzón es un incumplimiento silencioso. Algunos clientes antiguos solo usan esta vía.

## Qué mensajes la necesitan

Según las directrices de Google y Yahoo, la baja con un clic es obligatoria en **mensajes comerciales y promocionales** de remitentes masivos. Los transaccionales (recuperación de contraseña, recibos, alertas de seguridad) no la necesitan, y añadirla puede ser confuso: el usuario no puede "darse de baja" de un restablecimiento de contraseña.

## Errores comunes

- **Headers no firmados por DKIM.** El botón no aparece o Gmail ignora el header.
- **URL HTTP en lugar de HTTPS.**
- **Baja por GET**, con bajas fantasma por escáneres.
- **POST que redirige a una página** (`302`) o exige cookies.
- **Token reutilizable para cualquier destinatario** o adivinable.
- **Baja aplicada solo en la herramienta de marketing**, mientras otro sistema sigue enviando a esa dirección. La supresión debe ser central.
- **Buzón del `mailto:` sin procesar.**
- **Procesar en lotes semanales**, por encima del plazo de dos días.

## Checklist

- [ ] `List-Unsubscribe` con URL HTTPS (y opcionalmente `mailto:`).
- [ ] `List-Unsubscribe-Post: List-Unsubscribe=One-Click`.
- [ ] Ambos headers incluidos en `h=` de la firma DKIM.
- [ ] Token firmado con HMAC, por destinatario y lista.
- [ ] POST idempotente que responde `2xx` sin redirecciones.
- [ ] GET muestra confirmación, nunca ejecuta la baja.
- [ ] Buzón `mailto:` procesado automáticamente.
- [ ] Baja propagada a la lista de supresión central al instante.
- [ ] Enlace de baja visible en el cuerpo.

## En Pidgely

Pidgely añade los headers de RFC 8058 a los mensajes de los streams de marketing, los incluye en la firma DKIM del dominio y aloja el endpoint de baja con protección frente a escáneres. Cada baja entra en la lista de supresión y genera un webhook firmado para que tu aplicación pueda sincronizar sus preferencias.

## Preguntas frecuentes

### ¿Qué headers necesito para la baja con un clic?

List-Unsubscribe con al menos una URL HTTPS y List-Unsubscribe-Post con el valor exacto List-Unsubscribe=One-Click. Ambos deben estar cubiertos por una firma DKIM válida.

### ¿Puedo dar de baja al usuario cuando abre la URL con GET?

No es buena idea. Los escáneres de seguridad y los filtros visitan los enlaces con GET; si eso da de baja, perderás suscriptores que no lo pidieron. Solo el POST debe ejecutar la baja.

### ¿En cuánto tiempo debo procesar la baja?

Google y Yahoo piden procesarla en un plazo de dos días. Técnicamente lo ideal es que el POST la aplique de inmediato.

### ¿El endpoint puede pedir confirmación o inicio de sesión?

No. RFC 8058 exige que el POST complete la baja sin interacción adicional, sin cookies ni redirecciones que el cliente tenga que seguir.

