All articles

List-Unsubscribe=One-Click: The Header Pair That Has to Match

Guides·September 18, 2026·By The SendHustle Team·10 min read

The whole value of the List-Unsubscribe-Post header is one key/value pair and nothing else: list-unsubscribe=one-click. It is also the half of the pair that does the least work. The header that decides whether a mailbox provider shows an unsubscribe button next to your sender name is the other one, List-Unsubscribe, and unless it carries an HTTPS URI the pair means nothing at all.

That mismatch is where most broken implementations sit. Both headers are present, a syntax validator passes them, and the button still never appears — or it appears, someone taps it, and nobody gets unsubscribed. RFC 8058 is a short document and nearly every one of these failures traces back to six sentences in it. What follows is the failure list, not the definition.

What list-unsubscribe=one-click actually promises

The two headers do different jobs and fail for different reasons.

List-Unsubscribe is the older one, defined in RFC 2369, and it carries the destinations. Its contents are angle-bracket enclosed URLs, comma separated, and RFC 2369 is explicit that "the URLs have order of preference from left to right" — the client uses the leftmost protocol it supports. On its own it gets you the older two-step unsubscribe: the client opens the URL in a browser, the recipient lands on your page, and your page does whatever it does.

List-Unsubscribe-Post is the RFC 8058 addition, and it is a declaration rather than a destination. Section 3.1 says it "MUST contain the single key/value pair 'List-Unsubscribe=One-Click'". Sending it means: the HTTPS URI in the other header accepts a POST, and that POST completes the unsubscribe with no further interaction. A correct pair looks like this:

List-Unsubscribe: <https://send.yourdomain.net/u/9f3c1a2b4d>, <mailto:unsub@send.yourdomain.net?subject=unsub-9f3c1a2b4d>
List-Unsubscribe-Post: List-Unsubscribe=One-Click

Note what is load-bearing there. The HTTPS URI is first. The value of the second header is exact — no quotes, no extra parameters, no trailing semicolon. And the URI ends in a token that identifies one recipient on one list, because section 3.1 requires the URI to "contain enough information to identify the mail recipient and the list from which the recipient is to be removed".

Nine pairs that look correct and are not

Symptom Cause Fix
No unsubscribe button appears List-Unsubscribe holds only a mailto: Add an HTTPS URI; the POST header is meaningless without one
Button appears, nobody unsubscribes Endpoint answers the POST with a 301/302 Handle the POST at the exact URI; return 200
Button appears, nobody unsubscribes Endpoint renders "click to confirm" Complete the removal on the POST itself
POST returns 403 CSRF middleware or a login wall on the route Exempt the route; the POST carries no session
POST returns 415 Endpoint parses JSON only Accept multipart/form-data and application/x-www-form-urlencoded
Everyone unsubscribes to the same record One campaign-wide URL Mint a per-recipient token
Provider ignores the headers Message unsigned, or h= omits the two headers DKIM-sign the message and include both header names
Recipients vanish without clicking A GET on the URI performs the removal, and scanners prefetch links Make GET render a page; make POST do the work
Header present but malformed Value quoted, cased differently, or carrying extra params Emit List-Unsubscribe=One-Click byte for byte

The rest of this guide is the four of those that cost the most time to diagnose.

The redirect that silently disarms the whole thing

RFC 8058 section 3.1 is blunt about this: "The mail sender MUST NOT return an HTTPS redirect, since redirected POST actions have historically not worked reliably." A client that follows a 302 usually converts the POST into a GET on the way, so your endpoint receives a request with no body, no key/value pair in it, and no reason to treat it as a one-click removal. From the provider's side the request succeeded. From yours, nothing happened.

Almost nobody writes this redirect on purpose. It arrives from infrastructure:

  • A CDN or load balancer upgrading http:// to https:// — which also means the URI in the header must already be https://, not merely redirect there.
  • A trailing-slash canonicalisation rule, so /u/9f3c1a2b4d 301s to /u/9f3c1a2b4d/.
  • An apex-to-www (or www-to-apex) rule on the sending domain.
  • A framework's "force canonical host" middleware that fires before routing.

All four are invisible in a browser and fatal to the POST. Test the exact string you put in the header, not a tidied version of it.

A confirmation page is a failed unsubscribe

The spec's intent, in section 3.1, is that the sender can "handle it as a one-click unsubscription without manual intervention." There is no second step. If your POST handler responds with a page saying "are you sure?" or "click here to confirm", the removal did not happen, because the recipient is never going to see that page — the mail client made the request in the background and discarded the response body.

This is the failure mode that survives longest in production, because nothing errors. Your endpoint returns 200. Your logs show traffic. Your suppression list does not grow, and the complaints that the unsubscribe request was meant to prevent arrive anyway. The rule is simple: by the time you write the response, the recipient is already off the list.

The POST arrives stripped of everything your app expects

Section 3.1 again: "The POST request MUST NOT include cookies, HTTP authorization, or any other context information." The mailbox provider makes the request; the recipient's browser is not involved. So the request reaching your endpoint has no session cookie, no bearer token, no Referer your middleware recognises, and no CSRF token in the body — the body is only the key/value pair from the header.

In practice that means the unsubscribe route has to be exempted from the protections you apply everywhere else. In Rails that is skip_forgery_protection on the action; in Django, @csrf_exempt; in Laravel, an entry in the VerifyCsrfToken exception list; in Express, mounting the CSRF middleware after the route rather than globally. Skip that and your endpoint returns 403 to every provider that tries, forever.

The body encoding matters too. Section 3.2 says the POST content "SHOULD be sent as 'multipart/form-data' or MAY be sent as 'application/x-www-form-urlencoded'." An endpoint wired to a JSON-only body parser will reject both with a 415 and never reach your handler.

DKIM has to cover the headers, not just the message

Section 4 of RFC 8058 requires that "senders MUST apply at least one valid DKIM signature to the message." The reason is structural: without a signature, anything in the path could insert a List-Unsubscribe header pointing at a URL of its choosing, and a provider that acted on it would be handing unsubscribe traffic to a stranger.

The part that gets missed is the signed header list. A DKIM signature only covers the header names listed in its h= tag. If list-unsubscribe and list-unsubscribe-post are not in that list, the headers are technically unsigned even though the message is signed, and a provider is within its rights to distrust them. Check the h= tag of a real outgoing message — not the config you believe is deployed — and confirm both names are there.

Testing the pair before a campaign goes out

Two curl calls settle it. Use a real per-recipient URI from a message you actually received, not a hand-built one.

Check for redirects first:

curl -sS -o /dev/null -w '%{http_code} %{redirect_url}\n' \
  -X POST 'https://send.yourdomain.net/u/9f3c1a2b4d' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data 'List-Unsubscribe=One-Click'

A 200 with an empty redirect URL is what you want. Any 301, 302, 307 or 308 is the failure described above, and the redirect URL it prints tells you which rule caused it.

Then confirm the removal actually landed — query your own suppression table for that recipient. That is the check that catches the confirmation-page failure, and it is the one people skip because the HTTP status looked fine.

Worth repeating as a sequence:

  1. POST the exact URI from a delivered message, with the form-encoded body.
  2. Confirm 200 and no redirect.
  3. Confirm the recipient is suppressed in your own data.
  4. GET the same URI and confirm it renders a page rather than removing anyone.
  5. Inspect the delivered message's DKIM h= tag for both header names.

The header pair does not replace the link in the body

Google's sender guidelines tie one-click unsubscribe to volume: "If you send more than 5,000 message per day, your marketing and subscribed messages must support one-click unsubscribe." The same guidance requires those messages to "include a clearly visible unsubscribe link in the message body." The headers are in addition to the link, not instead of it — clients that do not implement RFC 8058 still need somewhere to send people.

Yahoo's sender best practices call the RFC 8058 POST method "highly recommended" and the mailto: method "acceptable", and attach a clock to both: "Honor unsubscribes within 2 days." A POST handler that writes to a queue processed weekly satisfies the letter of the header and misses the requirement that matters.

Where these rules are written down

Every requirement above comes from one of four documents, and all four are short enough to read in an afternoon. The two RFCs define the headers themselves; the two provider pages define what happens to your mail when you get them wrong. If a vendor tells you something that contradicts one of these, the document wins.

Frequently asked questions

Can List-Unsubscribe-Post be sent without an HTTPS URI in List-Unsubscribe?

No. RFC 8058 section 3.1 requires the List-Unsubscribe header to contain one HTTPS URI, and the POST header is a statement about that URI. If List-Unsubscribe carries only a mailto: address, the pair is incomplete and mailbox providers have nothing to POST to.

Why does the unsubscribe button appear but nobody gets removed from the list?

The two usual causes are a redirect and a confirmation page. RFC 8058 forbids answering the POST with an HTTPS redirect because redirected POSTs lose their body, and a handler that responds with a confirm page never completes the removal since the recipient never sees that page.

Does the POST include cookies or an authentication token?

No. Section 3.1 states the POST request must not include cookies, HTTP authorization, or any other context information. The mailbox provider makes the request, not the recipient's browser, so the unsubscribe route has to be exempt from CSRF protection and any login requirement.

What content type does the one-click POST body use?

RFC 8058 section 3.2 says the POST content should be sent as multipart/form-data or may be sent as application/x-www-form-urlencoded. An endpoint that only parses JSON will reject both with a 415 before your handler runs.

Is a visible unsubscribe link in the email body still required?

Yes. Google's sender guidelines require marketing and subscribed messages from senders above 5,000 messages per day to support one-click unsubscribe and to include a clearly visible unsubscribe link in the message body. The headers supplement that link rather than replacing it.

How quickly does an unsubscribe request have to be processed?

Yahoo's sender best practices say to honor unsubscribes within 2 days. Writing the request to a queue that is only processed weekly technically accepts the POST but misses that window, and the messages sent in the meantime are the ones that draw complaints.

Send your next campaign from Gmail

SendHustle brings mail merge, follow-ups, and tracking right into the inbox you already use.

Start free