One-click unsubscribe lets someone leave your mailing list directly from their mail client, without visiting a web page. It is built from two message headers, List-Unsubscribe and List-Unsubscribe-Post, and a small HTTPS endpoint that removes the recipient when it receives a POST request. Gmail and Yahoo require it from bulk senders of marketing mail, and it is one of the cheapest ways to lower spam complaints.
Two parts, both needed. The headers are for the mail client and the visible link in the footer is for people. Keep both. This guide is educational and is not legal advice: check the rules that apply in the regions where you send.
The short version
- Make a unique URL per recipient. An HTTPS link with a random token that identifies the recipient and the list.
- Add two headers.
List-Unsubscribe: <https://...>andList-Unsubscribe-Post: List-Unsubscribe=One-Click. - Handle the POST. Unsubscribe the recipient straight away and return a 2xx status with no redirect.
- Sign the headers with DKIM. Both headers must be in the signed header list.
- Test. Check that the control appears, a request works and the address stops receiving mail.
How one-click unsubscribe works
When a message arrives with the two headers, the mailbox provider can show an Unsubscribe control in its own interface, often next to the sender name. If the user taps it, the provider does not open your web page. It sends an HTTPS POST request straight to the URL in your header, with the body List-Unsubscribe=One-Click. Your server removes the recipient and answers with a 2xx status. The user sees a confirmation from their mail app, and no further steps are needed on your side of the experience.
| Step | Who acts | What happens |
|---|---|---|
| 1 | You | Send a message with List-Unsubscribe, List-Unsubscribe-Post and a valid DKIM signature |
| 2 | Mailbox provider | Verifies the signature and shows an Unsubscribe control |
| 3 | Recipient | Taps the control and confirms once in the mail app |
| 4 | Mailbox provider | Sends an HTTPS POST to your URL with List-Unsubscribe=One-Click |
| 5 | Your server | Looks up the token, suppresses the address and returns 200 OK |
The headers
List-Unsubscribe: <https://mail.example.com/u/8f3a1c9e7b>, <mailto:unsubscribe@example.com?subject=unsubscribe>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
| Part | Rule |
|---|---|
List-Unsubscribe | One or more URIs in angle brackets, separated by commas. The HTTPS URL is the one used for one-click. A mailto address is optional. |
List-Unsubscribe-Post | The value is exactly List-Unsubscribe=One-Click. It signals that a POST to the HTTPS URL unsubscribes immediately. |
| URL | HTTPS only, unique per recipient and per list, with a random token that cannot be guessed. Do not put an email address in the URL. |
| Number of headers | Send each header once. Duplicate headers make some providers ignore both. |
| DKIM | Both headers must be listed in the h= tag of the DKIM signature. |
Which messages need them
- Add them: newsletters, promotions, product announcements, digests and any mail a person subscribed to.
- Leave them off: password resets, receipts, security alerts, shipping notices and other messages the person needs to receive.
- Mixed messages: if a message is mostly promotional, treat it as marketing, even when an action triggered it.
Build the endpoint
The endpoint has one job: accept a POST, find the recipient from the token, suppress them and return a success status. It must not ask the person to log in, solve a captcha or click anything, and it must not redirect, because the mail client sends the request on its own.
PHP (CodeIgniter 4 style)
public function oneClick(string $token)
{
// The mail client sends a POST with body: List-Unsubscribe=One-Click
if ($this->request->getMethod() !== 'post') {
return $this->showConfirmPage($token); // GET shows a page with a button
}
$row = $this->subscribers->findByToken($token);
if ($row) {
$this->subscribers->suppress($row['id'], 'one-click'); // apply immediately
}
// Always answer 200, even for an unknown token, so nothing is leaked
return $this->response->setStatusCode(200)->setBody('OK');
}
Node.js (Express)
app.post('/u/:token', express.urlencoded({ extended: false }), async (req, res) => {
const row = await subscribers.findByToken(req.params.token);
if (row) {
await subscribers.suppress(row.id, 'one-click'); // apply immediately
}
res.status(200).send('OK'); // no redirect, no login
});
app.get('/u/:token', (req, res) => {
// GET only shows a confirmation page, because scanners open links
res.render('unsubscribe-confirm', { token: req.params.token });
});
Do not unsubscribe on GET. Link scanners and previewers fetch URLs automatically. If a GET request removes the address, they will unsubscribe people who never asked. Make the POST the one-click action and let a GET show a confirmation button.
Test the endpoint from the command line
curl -i -X POST -d "List-Unsubscribe=One-Click" https://mail.example.com/u/8f3a1c9e7b
You should see a 200 OK status and no Location header. Then check that the address now appears in your suppression list.
Sign the headers with DKIM
A mailbox provider only trusts the one-click headers when they are covered by a valid DKIM signature. In the signature, the h= tag lists the signed headers, and it must include both:
DKIM-Signature: v=1; a=rsa-sha256; d=example.com; s=mail1;
h=from:to:subject:date:message-id:list-unsubscribe:list-unsubscribe-post;
bh=...; b=...
If you send through a platform that signs messages for you, make sure your headers are added before signing. If you set the headers in your own application and the platform adds them again, you can end up with duplicates, so check the raw message. For the domain-level setup see authenticate a sending domain.
Suppression and records
- Apply it immediately. The two-day limit is a ceiling, not a target.
- Decide the scope. A request from a newsletter can unsubscribe the person from that list only, or from all marketing mail. State clearly which in your confirmation and preference page.
- Keep a record. Store the address, the list, the time and the source (one-click, link or reply) so you can show that you honoured the request.
- Never re-add. Imports and integrations must check the suppression list so unsubscribed people do not come back.
- Confirm gently. Where the law allows it, a short confirmation page with an option to re-subscribe is fine. Do not make the person work to leave.
Common mistakes
| Mistake | Result | Fix |
|---|---|---|
| Only a mailto address, no HTTPS URL | No one-click control | Add the HTTPS URL and the List-Unsubscribe-Post header |
| List-Unsubscribe-Post missing | Provider treats it as a plain link | Add List-Unsubscribe-Post: List-Unsubscribe=One-Click |
| Headers not covered by DKIM | Headers ignored | Add both to the h= list |
| Endpoint redirects or asks to log in | Request fails silently | Return 200 directly with no login |
| Unsubscribing on GET | Scanners remove real subscribers | Use POST for the action and a confirm page for GET |
| Same token for every recipient | One request removes everyone, or can be abused | Generate a unique random token per recipient and list |
| Requests processed in a weekly batch | More mail after unsubscribing, more complaints | Process on receipt |
| Header on transactional mail | Customers lose important messages | Leave it off password resets and receipts |
Test checklist
- Send to Gmail. Send a marketing test to a Gmail address and look for the Unsubscribe control next to the sender name.
- Read the source. Open Show original. Both headers should appear once, and DKIM should say PASS.
- Post to the endpoint. Use the curl command above and confirm a 200 response with no redirect.
- Confirm suppression. The address must be on your suppression list and excluded from the next send.
- Check the footer. The visible unsubscribe link in the body must work too.
- Watch the numbers. After launch, watch complaint rate. It should fall as people use the unsubscribe control instead of the spam button.
Need a second pair of eyes? Send us the raw header of a test message and we will check the unsubscribe headers and the DKIM signature.
Further reading and sources
Frequently asked questions
What is one-click unsubscribe?
One-click unsubscribe is a way for a mail client to unsubscribe a recipient without opening a web page. The sender adds two headers to the message, List-Unsubscribe and List-Unsubscribe-Post. The mailbox provider then shows an unsubscribe control, and when the user taps it the provider sends an HTTPS POST request to your URL. The standard is RFC 8058.
Is one-click unsubscribe mandatory?
Google and Yahoo require it from bulk senders for marketing and subscribed messages. It is not required for purely transactional messages such as password resets and receipts. Even for smaller senders it is a good idea, because a visible unsubscribe control reduces spam complaints.
How fast must I process an unsubscribe request?
Google and Yahoo ask senders to honour requests within two days. The safest approach is to apply them immediately so the person never receives a second message. Laws such as CAN-SPAM and CASL set their own deadlines, which are usually longer, but the mailbox provider rule is the stricter one in practice.
Do I still need an unsubscribe link in the email body?
Yes. The header is for the mail client, and the visible link is for people and for legal requirements. Keep a clear unsubscribe link in the footer of every marketing message.
What is the difference between List-Unsubscribe and List-Unsubscribe-Post?
List-Unsubscribe holds the address or URL the client may use to unsubscribe. List-Unsubscribe-Post, with the value List-Unsubscribe=One-Click, tells the client that the HTTPS URL accepts a POST request that unsubscribes immediately without further steps. You need both for one-click.
Can I use a mailto address instead of an HTTPS URL?
A mailto address still works as a fallback, but the one-click standard needs an HTTPS URL. Google and Yahoo expect the one-click headers from bulk senders, so provide the HTTPS URL, and add a mailto only if you want a second option.
Why must the headers be signed with DKIM?
RFC 8058 requires the message to carry a valid DKIM signature that covers both headers. This stops a third party from adding or changing the unsubscribe address in transit. If the headers are not signed, mailbox providers may ignore them.
Should a GET request to the unsubscribe URL unsubscribe the user?
It is safer if it does not. Security scanners and link previewers often open every URL in a message, and an unsubscribe on GET would remove people who never asked. Make the one-click action a POST, and let a GET show a confirmation page with a button.
How do I test one-click unsubscribe?
Send a message to a Gmail address and look for an Unsubscribe control next to the sender name. Open Show original to confirm both headers are present and that DKIM passes. Then use a command line tool such as curl to POST to the URL and check that the address is suppressed.
Do transactional emails need these headers?
No, but adding List-Unsubscribe to a transactional message can cause real users to unsubscribe from mail they need, so leave it off for password resets, receipts and security alerts. If a message mixes promotion with transactional content, treat it as marketing.