BookMySpot

Deliverability

One-Click Unsubscribe (RFC 8058) and the List-Unsubscribe Header

One-click unsubscribe lets a recipient opt out from a button in their mail app, with no landing page and no login. You enable it by adding two headers to every marketing email, List-Unsubscribe with an HTTPS URL and List-Unsubscribe-Post: List-Unsubscribe=One-Click, signing both with DKIM, and running an endpoint that unsubscribes the recipient when the mailbox provider sends it a POST request. Gmail and Yahoo require it for bulk marketing mail, and both expect the opt-out to be processed within two days.

This guide covers the standard, the exact header syntax, a working endpoint, the mistakes that make the button disappear, and how to test.

What RFC 8058 actually specifies

The List-Unsubscribe header has existed for decades. The problem was that an HTTPS link in that header was ambiguous. Did visiting the URL unsubscribe you, or show a page? Security scanners and link-preview bots would follow those links and accidentally unsubscribe people.

RFC 8058, published in January 2017, fixed this by adding a second header that tells the receiver “a POST to this URL performs the unsubscribe, no further interaction needed.” The key rules:

  1. List-Unsubscribe must contain one HTTPS URI. It can contain other URIs too, such as a mailto.
  2. List-Unsubscribe-Post must contain exactly List-Unsubscribe=One-Click.
  3. The URI must identify both the recipient and the list, so the unsubscribe can complete without asking anything. It should include a hard-to-forge component that your server checks.
  4. The message must carry a valid DKIM signature that covers both headers. If it doesn’t, the receiver shouldn’t offer one-click.
  5. The receiver sends an HTTPS POST to the URI with List-Unsubscribe=One-Click as the body.
  6. Your server must not redirect the POST, because redirected POSTs are often turned into GETs.
  7. The POST carries no cookies or authorization, so your endpoint can’t depend on a login session.

The exact headers to add

A compliant pair looks like this:

List-Unsubscribe: <https://example.com/unsubscribe/one-click/eyJ1IjoiNDIiLCJsIjoibmV3cyJ9.k3Jx9Q>, <mailto:unsubscribe+42.news@example.com>
List-Unsubscribe-Post: List-Unsubscribe=One-Click

Notes on the syntax:

  • Each URI is wrapped in angle brackets, separated by commas.
  • Put the HTTPS URI first. Including a mailto as a second option is fine and helps older clients.
  • The token in the URL should encode the subscriber and list and be signed (an HMAC or a random ID stored in your database). Never use a plain sequential ID like ?id=42, or anyone can unsubscribe anyone.
  • Header names are case-insensitive, but the value List-Unsubscribe=One-Click should be written exactly as shown.

Make sure DKIM covers both headers

Open a sent message’s source and find the DKIM-Signature header. The h= tag lists the signed headers:

DKIM-Signature: v=1; a=rsa-sha256; d=example.com; s=s1; c=relaxed/relaxed;
  h=from:to:subject:date:message-id:list-unsubscribe:list-unsubscribe-post;
  bh=...; b=...

If list-unsubscribe and list-unsubscribe-post aren’t in h=, the one-click button may not appear. Most email service providers add them automatically when you enable their one-click feature. If you run your own MTA, add both to the list of headers your DKIM signer covers. Also make sure the DKIM d= domain aligns with your From domain, which you need anyway for DMARC. See DKIM explained.

What Gmail and Yahoo require

RuleGmailYahoo
Who must complyBulk senders (around 5,000+ messages a day to Gmail)Bulk senders (no published volume threshold)
Which mailMarketing and promotional messagesMarketing and subscribed messages
MethodRFC 8058 headers; mailto and landing-page links don’t satisfy itRFC 8058 POST “highly recommended”; mailto accepted
Visible body linkRequiredRequired
Processing timeWithin 48 hours (recommended)Within 2 days

Sources: Google’s Email sender guidelines, Google’s sender guidelines FAQ, Yahoo’s sender best practices and Yahoo’s sender FAQ.

Transactional mail, such as password resets and order confirmations, is exempt from the one-click requirement at both providers. For the full rule set, see Gmail and Yahoo bulk sender requirements.

How this fits with CAN-SPAM

US law sets a looser floor. The FTC’s CAN-SPAM compliance guide requires you to honor opt-outs within 10 business days and keep your opt-out mechanism working for at least 30 days after you send. The mailbox-provider rules are stricter on timing, so design for two days. That also means your unsubscribe tokens must stay valid for at least 30 days, and in practice much longer, since people unsubscribe from old emails. For more on legal requirements, see consent, records and the rules that outlive your list.

Building a one-click unsubscribe endpoint

Your endpoint needs to handle two cases at the same URL:

  • POST from the mailbox provider: unsubscribe immediately and return 200.
  • GET from a human who clicked the link (or a bot that previewed it): show a confirmation page with a button. Don’t unsubscribe on GET, because link scanners will trigger it.

Here’s a minimal Node.js (Express) example:

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

const app = express();
app.use(express.urlencoded({ extended: false }));

const SECRET = process.env.UNSUB_SECRET;

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

app.post("/unsubscribe/one-click/:token", async (req, res) => {
  const data = verifyToken(req.params.token);
  if (!data) return res.status(400).send("Invalid token");
  if (req.body["List-Unsubscribe"] === "One-Click") {
    await suppress(data.u, data.l); // write to your suppression list
  }
  res.status(200).send("Unsubscribed"); // no redirect
});

app.get("/unsubscribe/one-click/:token", (req, res) => {
  // Show a page with a form that POSTs back to the same URL.
  res.send(`<form method="post"><input type="hidden" name="List-Unsubscribe" value="One-Click">
    <button>Unsubscribe</button></form>`);
});

RFC 8058 says receivers should send the body as multipart/form-data and may send application/x-www-form-urlencoded, so make sure your framework parses both. In Express that means adding a multipart parser (such as multer().none()) on the POST route, or checking the raw body for the string.

Endpoint checklist

  1. Respond within a few seconds. Do the heavy work (CRM sync, analytics) in a background job after writing the suppression.
  2. Return 200 or 202. No 3xx redirect.
  3. Don’t require cookies, CSRF tokens, or login on the POST.
  4. Make the operation idempotent. Providers may retry, and a second POST should be harmless.
  5. Write to your suppression list first, then sync it to every system that sends as your brand. See what belongs on a suppression list.
  6. Log the request with a timestamp so you can prove processing time.

Common reasons the unsubscribe button doesn’t show

Even with correct headers, Gmail and other providers decide whether to display their unsubscribe button. When it’s missing, check:

  • Headers not DKIM-signed, or signed by a domain that doesn’t align with your From domain.
  • List-Unsubscribe-Post missing or misspelled. A common typo is List-Unsubscribe=one-click with different casing, or extra spaces.
  • HTTP instead of HTTPS in the URI.
  • Duplicate headers, such as your ESP adding List-Unsubscribe and your template adding another. Google documents that Gmail blocks messages with duplicate headers of the same type for the headers it checks, so keep exactly one of each.
  • Low sending history. Providers don’t always show the button for every sender, especially new ones. That’s not something you can force.

How to test one-click unsubscribe

  1. Subscribe a test Gmail address and a test Yahoo address to your list.
  2. Send a real campaign through your normal platform.
  3. In Gmail, open Show original and confirm both headers and the DKIM h= tag.
  4. Use the unsubscribe option next to the sender name in Gmail (and in Yahoo webmail, which Yahoo’s FAQ recommends for testing).
  5. Check your server logs for the POST and your suppression list for the address.
  6. Send another campaign and confirm the test address is excluded.

You can also simulate the provider’s request with curl:

curl -X POST "https://example.com/unsubscribe/one-click/eyJ1IjoiNDIiLCJsIjoibmV3cyJ9.k3Jx9Q" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data "List-Unsubscribe=One-Click" -i

You should get a 200 with no Location header.

Why easy unsubscribes protect your deliverability

A subscriber who can’t find the unsubscribe link reaches for the spam button instead. Spam reports hurt your reputation, while unsubscribes don’t. Making the exit easy is one of the most direct ways to lower your spam complaint rate.

The other side of that is keeping your list clean so fewer people want out in the first place. That starts at signup. Our free email verifier checks syntax and MX records, flags disposable and role-based addresses, detects free-mail domains, and catches typos before they enter your list. It doesn’t contact the recipient’s mail server, so it can’t prove an inbox exists, but it stops the obvious bad data that leads to bounces and complaints later.

FAQ

Is a mailto List-Unsubscribe header enough?

Not for Gmail. Google’s FAQ says mailto links and landing-page links don’t meet the one-click requirement, so you need the HTTPS URI with List-Unsubscribe-Post. Yahoo accepts mailto but strongly recommends the RFC 8058 POST method. Including both covers every case.

The body link can lead to a page, which is fine. Both providers require a clearly visible unsubscribe link in the body in addition to the header. Keep that page simple, don’t require a login, and confirm the opt-out on the page.

Do I need one-click unsubscribe on transactional emails?

No. Gmail and Yahoo require it only for marketing and promotional mail. If your “transactional” emails include promotions, though, they may be treated as marketing.

How long should unsubscribe tokens stay valid?

At least 30 days to satisfy CAN-SPAM, but there’s no good reason to expire them. People unsubscribe from emails that are months old, and a dead link pushes them toward the spam button.

Check your list before you send

Free email verifier: syntax, MX, disposable and role-based addresses, typos. Paste up to 100 at once.

Verify emails free