Back to blog
Content··13 min read·WillItInbox Team

List-Unsubscribe Header: One-Click Unsubscribe Setup (RFC 8058)

Add List-Unsubscribe and List-Unsubscribe-Post headers, build the RFC 8058 POST endpoint, avoid common Gmail/Yahoo mistakes, and test compliance.

List-UnsubscribeRFC 8058GmailYahoo

Since February 2024, Gmail and Yahoo require bulk senders to honor one-click unsubscribe under RFC 8058. The header is two lines, the endpoint is one POST, and yet a remarkable number of senders still get it wrong — usually by treating it like a mailto link with extra steps. If you are fixing a campaign now, run a deliverability test and compare the result with the Gmail and Yahoo sender requirements checklist.

The two headers

Email headerstext
List-Unsubscribe: <mailto:[email protected]>, <https://example.com/u/abc123xyz>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
  • First header lists two methods: a mailto fallback and an HTTPS endpoint.
  • Second header signals RFC 8058 compliance — receivers will POST to the HTTPS URL automatically.
  • The mailto address is a fallback for clients that don't support one-click.

The POST endpoint contract

When a Gmail or Yahoo user clicks the native 'Unsubscribe' link, the receiver's server (not the user's browser) sends a POST request to your URL. There is no human in the loop and no chance to show a confirmation page. If you need to inspect a live header block first, use the free header analyzer.

What receivers sendhttp
POST /u/abc123xyz HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 26

List-Unsubscribe=One-Click

Implementation checklist

MistakeWhy it failsCorrect behavior
Only mailto unsubscribeMailbox clients cannot perform RFC 8058 one-click POSTInclude HTTPS URL plus mailto fallback
Confirmation required after POSTThe POST is the unsubscribe actionSuppress immediately and return 2xx
Shared or guessable tokenCreates privacy and abuse riskUse per-recipient opaque tokens
Suppression delayed past 2 daysProvider sees repeated unwanted mailApply suppression quickly across active sends
One-click unsubscribe mistakes that hurt placement.
  1. Generate a per-recipient HMAC token at send time. Never reuse tokens.
  2. Store the token mapped to the recipient address in your database.
  3. POST handler validates the token, suppresses the address, and returns 200 OK.
  4. Suppression must be effective within 2 days — same campaign already in flight should be aborted for that address.
  5. GET on the same URL should show a friendly confirmation page for users who paste the URL.
Token generation examplets
import { createHmac } from "node:crypto";

function unsubToken(recipient: string, secret: string): string {
  return createHmac("sha256", secret)
    .update(`unsub:${recipient}`)
    .digest("base64url")
    .slice(0, 32);
}

const url = `https://example.com/u/${unsubToken(to, process.env.UNSUB_SECRET!)}`;
// Add to headers:
// List-Unsubscribe: <mailbox fallback>, <${url}>
// List-Unsubscribe-Post: List-Unsubscribe=One-Click

Additional guidance: One-Click Unsubscribe (RFC 8058): Requirements, Headers, and Testing

One-Click Unsubscribe (RFC 8058): Requirements, Headers, and Testing: practical workflow
  1. 01Inspect

    Identify the affected sending stream.

  2. 02Decide

    Correct the durable cause, not the symptom.

  3. 03Verify

    Retest and monitor the resulting behavior.

The Gmail one-click unsubscribe requirement mandates that bulk senders (5,000+ messages/day to Gmail) include a working one-click unsubscribe in every marketing and subscribed message, implemented via the List-Unsubscribe and List-Unsubscribe-Post: List-Unsubscribe=One-Click headers per RFC 8058, with requests honored within 48 hours. Yahoo enforces the same. Non-compliance risks spam-foldering and rejection.

This is the rare compliance requirement that's actually good for you. Every unsubscribe that happens through the header is a spam complaint that didn't happen — and with complaint thresholds at roughly 0.1% at Gmail and 0.3% at Yahoo, the math favors making exit easy. This post covers the exact header syntax, the endpoint contract, what counts as in-scope, and how to test the whole path before a filter tests it for you.

What exactly is required, and by whom?

The requirement comes from the Gmail and Yahoo sender guidelines that took effect in 2024 and have been enforced with increasing teeth since — full rejection of non-compliant bulk mail is now standard, as covered in our breakdown of the Gmail and Yahoo bulk sender rules. The specifics:

  • Who: Anyone sending 5,000 or more messages per day to Gmail personal accounts. Yahoo applies the same threshold. Microsoft has not mandated RFC 8058 but honors List-Unsubscribe.
  • What: Marketing and subscribed messages must support one-click unsubscribe via RFC 8058 headers. Transactional mail is exempt (more on scope below).
  • How: Both List-Unsubscribe (with an HTTPS URL) and List-Unsubscribe-Post headers present; the URL must accept an unauthenticated POST that unsubscribes immediately.
  • When: The unsubscribe must be processed within 48 hours. Best practice is immediate — the 48 hours is a ceiling, not a target.

Note the threshold counts messages to the provider, not total send volume. 5,000 Gmail recipients a day makes you a bulk sender even if Gmail is 10% of your list. And the threshold is persistent once crossed — there's no "I only sent 4,000 today" exemption for domains already classified as bulk senders. Treat the requirement as permanent once you're anywhere near the line, which for any growing sender means: implement now, not when you hit the number.

The headers, exactly as they should appear

RFC 8058 builds on the older List-Unsubscribe header (RFC 2369) by adding a signal that says "POSTing to this URL performs the unsubscribe — no further interaction needed." A compliant pair looks like this:

List-Unsubscribe: <https://mail.example.com/unsub?op=one-click&rid=8f3a2c1e9b>
List-Unsubscribe-Post: List-Unsubscribe=One-Click

You can also include a mailto fallback alongside the HTTPS URL — some receivers still use it:

List-Unsubscribe: <https://mail.example.com/unsub?op=one-click&rid=8f3a2c1e9b>, <mailto:[email protected]?subject=unsubscribe>
List-Unsubscribe-Post: List-Unsubscribe=One-Click

Syntax details that matter:

  • The value of List-Unsubscribe-Post is the literal string List-Unsubscribe=One-Click. It's a flag, not a variable. Any other value means the receiver treats the header as absent.
  • The HTTPS URL should be unique per recipient (the rid parameter above). Never use a generic URL that requires the user to type their email address — that's a multi-click flow and doesn't comply.
  • Both headers must survive to the recipient intact, which means they must be included in your DKIM signature's h= list. An unsigned List-Unsubscribe header can be stripped or distrusted in transit; signing it also blocks replay-tampering of unsubscribe links.

The POST endpoint contract

This is where implementations fail. When a Gmail user clicks the Unsubscribe button, Gmail's servers send an HTTP POST to your URL. The contract:

1. The POST body is fixed and meaningless to you. It contains the one-click flag:

POST /unsub?op=one-click&rid=8f3a2c1e9b HTTP/1.1
Host: mail.example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 26

List-Unsubscribe=One-Click

All the identifying information lives in your URL parameters. The body just confirms intent per the RFC.

2. No authentication. The endpoint must not require login, cookies, a session, CAPTCHA, or any credential. Gmail's servers are the client — they can't log in. Any auth challenge and the unsubscribe silently fails while you stay non-compliant.

3. No confirmation step. The POST itself is the confirmation. Do not respond with "click this link to confirm," do not send a "confirm your unsubscribe" email, do not show a preference page first. One click, done. (A follow-up "you've been unsubscribed" notification email is permitted and fine UX — it just can't be a gate.)

4. Return 200. Respond with a successful HTTP status. Gmail checks this. 4xx/5xx responses, redirects to error pages, or timeouts mean the click didn't count.

5. Process within 48 hours. Suppress the recipient from all marketing streams within two days. Immediately is better — a user who clicked Unsubscribe and gets another campaign the next morning hits Report Spam instead, and now you have the worst of both paths.

6. The link must keep working. Don't expire unsubscribe URLs quickly. If a URL 404s when the provider or user hits it weeks later, that's a compliance failure on an old message. Keep the tokens valid for the life of the campaign at minimum.

Marketing vs transactional: what's in scope

The mandate covers marketing and subscribed messages — anything promotional, newsletters, product announcements, re-engagement campaigns. It exempts transactional mail: receipts, password resets, order confirmations, security alerts — messages triggered by a user's action or account state that they can't meaningfully opt out of while keeping the account.

Two warnings. First, the classification follows the content, not your intent. A shipping notification with a "you might also like" product carousel is marketing mail with a tracking number attached, and filters read it that way. Keep transactional streams clean; mixing promotional content into them is how transactional domains end up needing the kind of remediation described in our signup and password-reset deliverability guide.

Second, even for exempt transactional mail, having List-Unsubscribe doesn't hurt. What transactional mail must never do is require marketing consent or bundle opt-outs. Keep the streams separate — ideally on separate subdomains — so scope questions never arise.

Why the button beats the alternative

When one-click unsubscribe is present and trusted, Gmail renders an "Unsubscribe" link at the top of the message, next to the sender address. Yahoo does similar. This button is the best spam-complaint-prevention mechanism that exists, for one reason: it intercepts users at the exact moment of peak annoyance, before they reach for Report Spam.

The economics are stark. A spam complaint at Gmail counts against your ~0.1% threshold; enough of them and you're junked or rejected with 550 5.7.26 errors. An unsubscribe costs you one address you were never going to monetize anyway — disengaged recipients don't convert, and keeping them was pure downside risk. Given the choice between losing a subscriber and losing your domain reputation, take the unsubscriber every time.

There's a trust dimension too: Gmail is more likely to show the Unsubscribe button prominently for senders with good reputation, creating a virtuous cycle — easy exits, fewer complaints, better placement, more visible button. The opposite cycle is how domains end up in the spam folder wondering why.

Testing your implementation end-to-end

Don't assume your ESP got this right. Test:

1. Inspect the headers. Send yourself a campaign and view the raw headers. Confirm both headers exist, the Post header has the exact value List-Unsubscribe=One-Click, and the URL is HTTPS, recipient-specific, and signed by DKIM. The WillItInbox sender compliance checker validates this automatically along with the rest of the bulk-sender checklist — SPF, DKIM, DMARC alignment, and the unsubscribe headers in one pass.

2. Simulate the provider's POST. Don't click with a browser — browsers GET. Reproduce what Gmail actually does:

bash
curl -i -X POST "https://mail.example.com/unsub?op=one-click&rid=8f3a2c1e9b" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "List-Unsubscribe=One-Click"

You're checking for: HTTP 200, no redirect chain, no auth challenge, and — critically — the recipient actually suppressed in your list afterward. A 200 that doesn't unsubscribe is the most dangerous failure mode: everything looks compliant and nothing works.

3. Verify suppression timing. After the POST, confirm the address is excluded from the next campaign send, not just flagged in a queue. Test with a live small segment.

4. Watch real behavior. In Gmail, a compliant message shows "Unsubscribe" next to the sender name when reputation permits. If your headers are right and the button never appears, that's a reputation signal, not a header bug.

5. Re-test after changes. ESP migrations, template rebuilds, and DKIM changes (if you drop the headers from the h= list) all break this quietly. Add it to the compliance re-check you run with the sender compliance checker whenever sending infrastructure changes.

Common failure modes we see in the wild

  • GET-only endpoints. Built for humans clicking footer links; the provider's POST gets a 405. Support both verbs on the same URL.
  • Expired tokens. Unsubscribe URLs keyed to a campaign ID that gets purged after 30 days. Old mail fails compliance retroactively.
  • 200 but no suppression. The endpoint acknowledges the POST but the unsubscribe lands in a queue that syncs nightly — or never. Verify the list state, not the HTTP status.
  • Headers added after signing. A downstream relay injects List-Unsubscribe post-DKIM, so the headers are unsigned and distrusted. Inject before signing, always.
  • Multi-platform gaps. The marketing ESP handles one-click perfectly; the "lifecycle tool" sending weekly digests doesn't add the headers at all. Every sending platform is in scope, not just the biggest one.

ESP vs self-hosted setups

If you use a mainstream ESP (Mailchimp, SendGrid, Braze, Customer.io, etc.), RFC 8058 headers are almost certainly handled for you — it's table stakes since 2024. Your job is verification, not implementation: check that the headers appear on your campaigns (some platforms only add them to certain message types), and confirm the unsubscribe actually syncs back to your suppression lists, especially if you send from multiple platforms. A user who unsubscribes via Platform A and keeps getting Platform B's mail is a complaint waiting to happen.

If you self-host (Postfix, PowerMTA, custom SMTP), you own the whole chain: inject both headers at send time with a signed, recipient-specific URL; stand up the endpoint; wire it to your suppression logic; sign the headers with DKIM. The endpoint is deliberately trivial — the curl example above is the entire integration surface. The work is in token management (make URLs unguessable and long-lived) and in suppression propagation across every system that can send.

If you're on WordPress/WooCommerce or similar, unsubscribe handling depends on which plugin sends your marketing mail. Transactional plugins won't add these headers — which is correct for receipts, but a problem the moment a "marketing" plugin sends without them. Audit per-plugin.

One adjacent hygiene item: one-click unsubscribe doesn't replace honoring replies. Users still reply "remove me" to mail — and if you send from a no-reply address, those requests vanish into the void while complaints pile up. Our post on why no-reply addresses cause deliverability problems makes the full case, but the short version is: RFC 8058 handles the scaled exit, a monitored reply address handles the human one. You need both. And on the flip side of the lifecycle, clean acquisition — think double opt-in where it fits — means fewer people ever reach for that button in the first place.

Before you go: documentation says what your headers should contain, but the only way to know what your messages actually advertise is to look at one on the wire. Send a real email through the email deliverability tester and it will show you exactly which List-Unsubscribe headers the receiving side sees — and whether they're DKIM-signed.

Frequently asked questions

Last updated August 2, 2026.

Sources reviewed

Factual review: June 13, 2026 by WillItInbox Editorial.

Keep reading