Short answer: One-click unsubscribe needs two headers in every marketing message: List-Unsubscribe: <https://...> with an HTTPS URL that identifies the recipient and list, and List-Unsubscribe-Post: List-Unsubscribe=One-Click. Both must be covered by a valid DKIM signature, and the URL must accept a POST and answer 200 without redirects. The sending application adds the headers; Exim or Postfix only has to sign them. Gmail and Yahoo require it for bulk senders and expect unsubscribes to be honoured within two days.
We ran the Exim DKIM checks on our lab servers (AlmaLinux 9.8 with cPanel & WHM 11.138 and DirectAdmin 1.712, Exim 4.100.1) and tested the example endpoint below with PHP 8.2 and curl on the cPanel lab on 6 October 2026. The Postfix/OpenDKIM part was checked against the OpenDKIM documentation and source, not run on a lab server.
Table of Contents
What RFC 8058 requires
The headers look like this (the mailto: part is optional):
List-Unsubscribe: <https://example.com/unsubscribe.php?l=news&e=Ym9iQGV4YW1wbGUuY29t&t=9f2c...>,
<mailto:unsubscribe@example.com?subject=unsubscribe>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
When a user clicks the unsubscribe button in their mailbox, the provider sends an HTTPS POST to that URL with the body List-Unsubscribe=One-Click. The rules from RFC 8058:
List-Unsubscribemust contain one HTTPS URI. It may also contain amailto:.List-Unsubscribe-Postmust contain exactlyList-Unsubscribe=One-Click.- The URI must carry enough information to remove the right recipient from the right list, because the POST has no other parameters. It should include an opaque or hard-to-forge token.
- The message must have a valid DKIM signature whose
h=tag includes both headers. Without it, receivers should not offer one-click. - The server must not answer the POST with a redirect, and the request carries no cookies or login.
Gmail adds that a mailto link or a plain “unsubscribe” link in the body does not meet its one-click rule. Yahoo calls the POST method “highly recommended” and accepts mailto. Both want unsubscribes honoured within 48 hours / 2 days, and both require it only for marketing and subscribed mail, not for receipts or password resets.
Who adds the headers
Exim and Postfix do not know which messages are newsletters or who the recipient is on a list, so they cannot create correct per-recipient unsubscribe URLs. The headers must come from the software that builds the message: the newsletter plugin, CRM, mailing list manager or your own code. The server admin’s jobs are to make sure DKIM signs those headers, and that the unsubscribe URL works through the web stack.
Exim on cPanel and DirectAdmin: check DKIM covers the headers
Exim signs the headers listed in dkim_sign_headers. When that option is not set, it uses a built-in default. Check what your build uses:
exim -bP macro _DKIM_SIGN_HEADERS | tr ':' '\n' | grep -i list
grep -c dkim_sign_headers /etc/exim.conf
Output on our cPanel lab (the DirectAdmin lab showed the same list):
List-Id
List-Help
List-Unsubscribe
List-Unsubscribe-Post
List-Subscribe
List-Post
List-Owner
List-Archive
0
The 0 means neither panel’s Exim configuration overrides the option, so Exim 4.100.1 signs List-Unsubscribe-Post whenever the message contains it. cPanel signs with selector default and DirectAdmin with x; DKIM must be enabled for the sending domain. If you added your own dkim_sign_headers line in a custom Exim configuration, make sure it still includes both list headers.
Postfix with OpenDKIM
OpenDKIM’s documented default for SignHeaders is the “common examples” list from RFC 6376 section 5.4.1, which includes List-Unsubscribe but predates and does not name List-Unsubscribe-Post. Check a signed message first (see the verification section). If list-unsubscribe-post is missing from h=, set the list explicitly in /etc/opendkim.conf:
SignHeaders From,Reply-To,Subject,Date,To,Cc,Message-ID,MIME-Version,Content-Type,Content-Transfer-Encoding,In-Reply-To,References,List-Id,List-Help,List-Unsubscribe,List-Unsubscribe-Post,List-Subscribe,List-Post,List-Owner,List-Archive
Specifying a list replaces the default entirely, so include everything you want signed. Then restart OpenDKIM (systemctl restart opendkim) and send a new test message. If you sign with Rspamd or another milter instead, check its signed-headers setting the same way.
WordPress and PHP senders
Newsletter plugins and mailing services usually have a setting for List-Unsubscribe; check the plugin’s documentation for “List-Unsubscribe-Post” or “one-click” specifically, since older versions often add only the first header. If you send from your own code, wp_mail() accepts extra headers and passes unknown ones through to PHPMailer:
<?php
// Newsletter sent from your own WordPress code
$unsub = 'https://example.com/unsubscribe.php?l=news&e=' . $e . '&t=' . $t;
$headers = [
'From: Example News <news@example.com>',
'List-Unsubscribe: <' . $unsub . '>, <mailto:unsubscribe@example.com?subject=unsubscribe>',
'List-Unsubscribe-Post: List-Unsubscribe=One-Click',
];
wp_mail($to, $subject, $html, $headers);
With plain PHPMailer, use $mail->addCustomHeader('List-Unsubscribe-Post', 'List-Unsubscribe=One-Click'). Only add these headers to marketing mail, never to order confirmations or password resets, or users will unsubscribe from mail they need.
A minimal unsubscribe endpoint
This example endpoint checks an HMAC token, unsubscribes on POST, and shows a confirmation button on GET (link scanners and spam filters fetch URLs with GET, so a GET must never unsubscribe on its own). Replace the log line with your real list update and set a long random secret.
<?php
// unsubscribe.php - minimal RFC 8058 one-click endpoint (example)
// URL in mail: https://example.com/unsubscribe.php?l=news&e=<base64url(address)>&t=<hmac>
const SECRET = 'change-this-long-random-secret';
function b64url_decode(string $s): string {
return (string) base64_decode(strtr($s, '-_', '+/'));
}
$list = $_GET['l'] ?? '';
$email = b64url_decode($_GET['e'] ?? '');
$token = $_GET['t'] ?? '';
$valid = $list !== '' && filter_var($email, FILTER_VALIDATE_EMAIL)
&& hash_equals(hash_hmac('sha256', $list . '|' . $email, SECRET), $token);
if (!$valid) {
http_response_code(400);
exit('Invalid unsubscribe link');
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// One-click: mailbox provider POSTs "List-Unsubscribe=One-Click"
if (($_POST['List-Unsubscribe'] ?? '') !== 'One-Click') {
http_response_code(400);
exit('Bad request');
}
// Replace with your real list update (database, newsletter API...)
file_put_contents(__DIR__ . '/unsubscribed.log', date('c') . " $list $email\n", FILE_APPEND | LOCK_EX);
http_response_code(200);
exit('Unsubscribed');
}
// GET: show a confirmation button instead of unsubscribing, because link
// scanners and spam filters fetch URLs with GET.
header('Content-Type: text/html; charset=utf-8');
echo '<form method="post"><input type="hidden" name="List-Unsubscribe" value="One-Click">'
. '<button type="submit">Unsubscribe ' . htmlspecialchars($email) . '</button></form>';
On our lab, a POST with application/x-www-form-urlencoded and one with multipart/form-data (the two encodings RFC 8058 allows) both returned HTTP/1.1 200 OK, a GET returned the confirmation page with 200, and a POST with a tampered token returned 400.
Check that it worked
- Send a real campaign message to a Gmail or Yahoo test mailbox and view the original source.
- Confirm both headers are present exactly once.
- Find the
DKIM-Signatureheader for your domain and check that itsh=list containslist-unsubscribeandlist-unsubscribe-post. Our Email Header Analyzer shows the signature details. - Test the URL like a mailbox provider would, from outside your network:
curl -s -i -X POST -d 'List-Unsubscribe=One-Click' 'https://example.com/unsubscribe.php?l=news&e=...&t=...'
You want 200 and no Location: header, and the address should disappear from the list. Then send another campaign and make sure that address is skipped. Gmail may still not show its own Unsubscribe button at the top; it does so only for senders that pass its eligibility and reputation checks.
Common problems
- 301 or 302 on the POST. An http-to-https rule, a www redirect, or WordPress’s canonical URL redirect (for example a missing trailing slash). RFC 8058 forbids redirects; put the exact final URL in the header.
- 403 from ModSecurity, Imunify360 or Cloudflare. Firewalls may block a POST with no cookies and an unusual user agent, or show a browser challenge. Add an exception for the unsubscribe path only.
- Cached responses. Page-cache plugins and CDNs must bypass the unsubscribe URL, or POSTs may never reach PHP.
- Headers present but not signed. The headers were added after signing, for example by a relay or a footer-adding gateway. Sign at the last hop that changes the message.
- Header added twice. A plugin and the ESP both add
List-Unsubscribe. Keep one. - Unsubscribes not processed in time. Gmail and Yahoo expect removal within 48 hours. Process POSTs immediately rather than in a weekly batch.
Official documentation: RFC 8058: one-click unsubscribe · Gmail email sender guidelines FAQ · Yahoo Sender Best Practices · WordPress: wp_mail()
Related: Email Header Analyzer · WordPress MailBaby SMTP Plugin: Setup and Testing · SPF, DKIM and DMARC in cPanel DNS: Setup and Checks · MailBaby Newsletter Sending Limit: 6,000/Hour Without Spam Problems · Postfix MailBaby Smarthost on Ubuntu and Debian: Secure Setup
See also: Gmail and Yahoo Bulk Sender Requirements: 2026 Host Checklist · Outlook 550 5.7.515: Fix the High-Volume Sender Rejection · Emails Going to Junk in Outlook and Hotmail: Sender-Side Fix · Microsoft SNDS and JMRP Setup: 2026 Portal, Access and Reports · IP Warm-Up Schedule Generator for New Mail Servers
Frequently asked questions
What is the List-Unsubscribe-Post header?
A header defined by RFC 8058 that must contain exactly List-Unsubscribe=One-Click. It tells mailbox providers they can unsubscribe the user with a single HTTPS POST to the List-Unsubscribe URL.
Is a mailto List-Unsubscribe enough for Gmail?
No. Gmail says mailto links and body links do not meet its one-click requirement. You need an HTTPS URL plus the List-Unsubscribe-Post header. Yahoo accepts mailto but recommends the POST method.
Does the List-Unsubscribe header need to be DKIM signed?
Yes. RFC 8058 requires a valid DKIM signature that includes both List-Unsubscribe and List-Unsubscribe-Post in its h= tag.
Can my mail server add one-click unsubscribe for all customers?
Not correctly. The URL must identify the recipient and the list, which only the sending application knows. The server should just sign the headers the application adds.
Do transactional emails need one-click unsubscribe?
No. Gmail and Yahoo require it only for marketing and subscribed messages. Leave it off password resets and receipts.
How fast must I process an unsubscribe?
Gmail recommends within 48 hours and Yahoo says within 2 days. Processing the POST immediately is the safest approach.