But I like this review of techniques, even the simplest ones are very effective, that surprised me.
<span class="hidden email"><b>999a8f84898f98</b>aa<b>878b8386c4</b>999a8f84898f988785989e8f84998f84c4898587</span>Anecdotal, but I’ve used HTML entities on a public static website for a long time using an href tag with mailto, and yet I’ve not seen any spam.
I guess any spammer who uses some level of GenAI to process and extract email addresses would have a lot more success against all the methods listed in this article.
The data-source are the enormous data breach that are more and more frequent. There is more intensive to collect more information on someone you already know something about than spamming an email you don't even know if it's a valid one.
The spam can also be very more effective as it present itself with personal information about the spammed.
Edit: that’s not to deny that big data leaks are a serious problem
Also, a note to those who make fancy "me+someservice@somedomain.com" addresses: make really sure you are in control and these work. Some services (including mine) will need to E-mail you one day, for example to tell you that your account will be deleted because of inactivity. If you don't receive that E-mail because of your fancy spam defenses, your account will be deleted. I've seen people hurt themselves like this and it makes me sad.
On a constructive note: what works very well is spam filtering using LLMs. We have AI to help us with this problem today. I wrote an LLM despammer tool which processes my inbox via IMAP using a local LLM (for privacy reasons). I see >97% accuracy in my benchmarks on my (very difficult) testing corpus. It's nearly perfect in real life usage. I've tested many local models in the 4-32B range and the top practical choice is gpt-oss:20b (GGUF, I run it from LM Studio, MLX quantizations are worse) — not only does it perform very well, but it's also really fast.
Contact details: [any mailbox] [at] [the domain name of this web site]. Please don’t ask me to give interviews, sign books, appear on podcasts, attend conferences or conventions, or provide feedback or endorsements for works of fiction, scientific theories, or slabs of text disgorged by chatbots.
I have no idea how to decipher this obfuscation.
Just wait until one of these companies demands an email from the registered email address of your account!
Also, the two can be complementary, anyways, so I am not sure what your point is.
If you use a catch-all on a domain, i.e. someservice@somedomain.com, I guess in theory that might break. But it seems about as likely as messing up the overall domain setup.
Also, my account on your service is likely much more disposable to me than my email address/domain. Anything I care about, I'd back up. Not just assume some random website is going to preserve it for me forever.
When I view a commit on the github UI using view source, I can see the commit author's email address just as text with no special handling. It's bracketed by "<" and ">", so maybe that's enough to confuse harvesters.
I just looked at the spam folder of one my personal accounts (where I sign up for services), and it has got tons of stuff, most recently 2 or 3 with the subject "YOU PERVERT! I RECORDED YOU!".
It seems spammers are doing less harvesting and more purchasing of email lists from service vendors.
Then you can hand each recipient an absolutely unique email which isn't just ole "name.morewords@" period trick — block those which receive SPAM.
----
OR: the even "easier" lifestyle of just not using email (like me). Obviously this is difficult for modern living, but that's what temp email is best for [i.e. circumventing ubiquitous `REQUIRED` email address fields].
(Similarly, I'm sure most links can be found by searching the bytestring for "href" and taking what's to the right of it.)
This would explain why HTML entities are so effective.
On the other hand, surely the TLS handshake is far more expensive than HTML parsing? Maybe it's to avoid parser failure modes that consume a lot of resources?
Like others mentioned, though, personally i haven't bothered by email harvesting for years now since spam filters seem to do a decent job. I have my email posted in plaintext here (which i bet is harvested very often) and in various other places and the occasional spam i get is eclipsed from "spam" from services i've actually signed up for (coughlinkedincough).
Something like:
``` <a href="#" class="js-mailto ${className}" data-email-user="${local}" data-email-host="${host}" data-email-subject="${sub}" > ${children} </a> ```
And then some light vanilla JS to stitch it together. Works in the browser, and spam has dropped off a cliff since.
For a similar reason I dislike ip2ban, my objective is not to block all attack attempts, I prefer receiving them acknowledging them and being immune to them.
The idea of ignoring attack attempts isn't very safe when you think about it, your body doesn't do that, it creates antibodies upon subclinical expositions. Complete isolation means your immune system is weak and you are more vulnerable to the lightest of exposures.
Basically each email gets written as a brainf*ck program and stored in a "data-" attribute. The html only includes a more primitively obfuscated statement "Must enable Javascript to see e-mail." by default which then gets replaced by another brainf*ck interpreter (in JS) with the output of the brainf*ck code. Since we only output ASCII we can reduce the size of the brainf*ck code by always adding 32 to each value it outputs. The Javascript is loaded from what seemingly looks like a 3rd party domain. There we filter basing on heuristics and check if the "referer" matches before sending out the actual interpreter code.
Of course all this would not help if a scraper properly runs things through Javascript too.
Recently I read you soon will be able to run DOOM via CSS, so certainly it should be possible to have a brainf*ck interpreter in CSS? That would be the next step… just to get rid of the Javascript, but then I'm okay with all the downsides of using Javascript just for the e-mail obfuscation.
Anyway… I also regularly (at least once a year) rotate those public contact addresses.
- git@mydomain.com
Presumably harvested from GitHub or gitlab
- contact@mydomain.com / admin@mydomain.com
Not actually an email address ever used, presumably people just guessing these exist from convention.
- <first name>@mydomain.com
I mean, if you know my name you can probably guess this but also this has been my primary email address for outbound email and so has ended up in marketing lists etc.
- ap@mydomain.com, finance@mydomain.com
This is a very recent trend but I've been getting emails to made up addresses like these ones quoting forged emails from myself (with various titles like CEO or CFO attached) claiming to authorize payments to other parties, usually backdated, and then asking that I process their invoice ASAP because look how long ago the CEO said it should be paid. I guess my website has ended up in some list of businesses despite being a personal site.
Ironically, the address that was in plain text in my HN profile for like 15 years gets very minimal spam.
I occasionally get spam from people who took the time to create gmail accounts. Based on this advice, the honey pot email address would get spam from a Gmail account and your script would block Gmail servers.
Could also be that they learned that sending spam to obfuscated addresses doesn’t gets much response. Such messages might get filtered out more and/or addressees might be less inclined to reply to it.
Then I hit upon a simpler solution. Have one email address. Happily share publicly. And whitelist the sender's email addresses. Emails not in the whitelist go into a quarantine folder that I glance at once in a while.
It's almost equivalent in efficacy, but much simpler to implement.
This article however is talking about publishing your email address on a public website. It matches my experience, that simple javascript concatenation stops 100% of spam. Not that I would or ever did trust my primary email address to that.
A dog will keep biting long after that is a disastrous plan.
Imagine someone visiting your blog who wants to e-mail you can burn some CPU cycles to "earn" an address that hasn't been given out to anybody else, e.g. user+TOKEN@example.com, where it is algorithmically-unlikely for them to be able to guess a different TOKEN that will work. Then if abuse occurs, you can just retire that one address. (In a non-interactive context, like a paper ad, you could just generate one yourself.)
Naturally, this would be best with an e-mail client that is aware of the scheme, and with a mail-service that has some API for generating new addresses, such as if you want to cold e-mail somebody and use a new from/return address.
Some years ago I had the fanciful idea of doing it with a phone-app, where it manages creating new addresses as-needed, disabling them, and keeping notes about who you gave them to.
When configured correctly each family member can reach you at a custom handle@, even seeing this custom reply address in response emails from you.
----
But yes, you're correct about the purpose of OP's article (website obfuscation). The topic-overlap is so close that it's still worth mentioning, IMHO.
I use it all the time in conjunction with Bitwarden to generate unique emails per site. You can have notes in each email, and they show up in a small banner on in the forwarded email. And each one is individually disable-able, so you can easily cut it off if you see spam from it.
I was really interested in this space and made my own homegrown tool for this. I used it for a while until I discovered Addy and switched over. IIRC there are similar services by Mozilla, Apple, and Proton.
/edit
And you can combine both approaches: XOR'ing the code first for good measurements. :)
Last updated:
January 30, 2026
Here are some of the best techniques for keeping email addresses hidden from spammers—along with the statistics on how likely they are to be broken.
These techniques protect an email address written out in plain text (e.g. “email@example.com”).
Ideally, you should be using multiple techniques in combination, by splitting the email address into segments, where each segment is protected by a different technique.
Blocked 0% of 318 spammers
HTML
aa@email.spencermortensen.com
Browser
Blocked 95% of 318 spammers
HTML
ab@email.spencermortensen.com
Browser
HTML entities are often decoded automatically by server-side libraries, which means that even the most basic harvesters can get your email addresses without any special effort. This technique should be worthless—and, yet, it still stops most harvesters.
Blocked 98% of 318 spammers
HTML
ac@email.spencermortensen<!--.example-->.com
Browser
This will stop only the most basic harvesters that struggle with HTML tags. It offers minimal protection—and, yet, still manages to stop most harvesters (since most harvesters are basic).
Blocked 100% of 318 spammers
HTML
<object class="email" width="130" height="24" data="email.svg" type="image/svg+xml"></object>
SVG: email.svg
<svg viewBox="0 0 130 24" xmlns="http://www.w3.org/2000/svg"> <style> @import url('https://fonts.googleapis.com/css2?family=Indie+Flower&display=swap'); text { dominant-baseline: middle; fill: #000; font-family: 'Indie Flower'; font-size: 16px; text-anchor: middle; } </style> <text x="50%" y="50%">email@example.com</text> </svg>
CSS
object.email { height: 2em; margin: -1em 0; vertical-align: middle; }
Browser
Inspiration: rouninmedia.github.io
This hides the email address in an unusual place where most harvesters won’t think to look. However, the email address is stored there in plain text.
This technique is accessible to all users, including those who depend on a screen reader. But you must use an object element for this to work: an img element would give you an image that is non-interactive, and inline SVG would put the email address in the source code where harvesters would find it easily.
The “width” and the “height” attributes help prevent layout shifts while the page is loading. Because the dimensions depend on the font, the SVG file has to explicitly specify a web font to prevent rendering issues.
Blocked 100% of 318 spammers
HTML
<div class="email">ad@<span>email.</span>spencermortensen.<span>example.</span>com</div>
CSS
div.email > span:nth-child(2) { display: none; }
Browser
ad@email.spencermortensen.example.com
Most harvesters are unable to apply style rules, so this is one of the absolute best techniques. Be sure to vary the decoy tags so the harvester won’t know which parts to omit.
This is fully accessible to everyone, including those who depend on a screen reader. But you must use “display: none” to hide the text: if you use any visual-only technique (such as shrinking the font size, or repositioning the text off screen), then you’ll break accessibility.
Blocked 100% of 318 spammers
HTML
<script>document.write('a'+'i'+'@'+'e'+'m'+'a'+'i'+'l'+'.'+'s'+'p'+'e'+'n'+'c'+'e'+'r'+'m'+'o'+'r'+'t'+'e'+'n'+'s'+'e'+'n'+'.'+'c'+'o'+'m');</script>
Browser
This is convenient because it has no external dependencies, and yet still manages to block most harvesters. However, the full email address appears directly in the HTML source code, so this technique cannot be considered safe.
Blocked 100% of 318 spammers
HTML
<span class="email">nw@rznvy.fcraprezbegrafra.pbz</span>
HTML
<head> <script src="[text-rot18.js](https://spencermortensen.com/articles/email-obfuscation/files/text-rot18.js)" defer></script> </head>
Browser
This technique can be undone by basic harvesters that don’t interpret JavaScript. At the very least, you should rotate your letters by something other than 13, and rotate your numbers by something other than 5.
Blocked 100% of 318 spammers
HTML
<span id="text-conversion">zibby example com</span>
HTML
<head> <script src="[text-conversion.js](https://spencermortensen.com/articles/email-obfuscation/files/text-conversion.js)" defer></script> </head>
Browser
zibby example com
In this technique, the HTML source code contains gibberish, and you write a custom function that converts the gibberish into a working email address.
Most harvesters can only access the HTML source code—and the source code contains nothing of value. The only practical way to restore the email address is to run your custom conversion function in a web client with DOM and JavaScript support. This is not possible for most harvesters.
You can write a custom function for each email address, or you can write a single function and apply it to all of the email addresses on the page.
Despite being frighteningly simple, this is expected to be one of the very best techniques.
Blocked 100% of 318 spammers
HTML
<span class="email">Kreuz2xa6xB8Fpjaa0lFgACNLO6n_Auu1CGjcG8z_Ec</span>
HTML
<head> <script src="[text-aes.js](https://spencermortensen.com/articles/email-obfuscation/files/text-aes.js)" defer></script> </head>
Browser
Kreuz2xa6xB8Fpjaa0lFgACNLO6n_Auu1CGjcG8z_Ec
This technique uses AES 256 to encrypt the email address. (AES is the only publicly-available cipher approved by the NSA for top secret information.) The email address cannot be recovered without the JavaScript file—which most harvesters cannot access or run. This implementation uses the browser’s own built-in cryptography library, so it will not run outside of the browser, even in JavaScript-capable environments.
The cryptography library, SubtleCrypto, is only available in secure contexts (such as over https or on localhost)—which could be a blocker if you’re using http. You must upgrade to https before you can use this technique!
Blocked 100% of 318 spammers
HTML
<span id="text-interaction">whose baby example com</span>
HTML
<head> <script src="[text-interaction.js](https://spencermortensen.com/articles/email-obfuscation/files/text-interaction.js)" defer></script> </head>
Browser
whose baby example com
This technique keeps the email address hidden until the user interacts with the page; only then is the email address revealed. This raises the bar for harvesters: they not only need to run a full web client, they also need to interact with it.
This technique can be used to trigger other techniques.
Blocked 97% of 318 spammers
HTML
ag AT email DOT spencermortensen DOT com
Browser
ag AT email DOT spencermortensen DOT com
This technique is well known, and easily reversible, so it cannot be considered safe.
Breaks usability. This forces the user to undo every substitution before they can send their email.
Blocked 100% of 318 spammers
HTML
au.fluff@email.spencermortensen.com (remove the “.fluff” before writing to me)
Browser
au.fluff@email.spencermortensen.com
(remove the “.fluff” before writing to me)
In general, only a human or an AI can break this. It’s mainly useful for when you need to publish your email address on an untrusted site.
This is at minimum inconvenient for your users, and may prevent them from reaching you at all.
Breaks usability. The user has to understand and follow the instructions perfectly, or they will be unable to reach you.
Blocked 100% of 318 spammers
HTML
<img src="[email.jpg](https://spencermortensen.com/articles/email-obfuscation/images/text-image.jpg)" width="216" height="18" alt="email address">
Browser

This is inconvenient or inaccessible for every one of your users.
Breaks usability. Sighted users are forced to type out the full email address by hand. The remaining users have no way to reach you.
Blocked 100% of 318 spammers
HTML
<span class="email" data-user="af" data-domain="email.spencermortensen.com"></span>
CSS
span.email::after { content: attr(data-user) '@' attr(data-domain); }
Browser
This breaks basic usability (e.g. the text can be seen, but not copied), so it is worthless.
It is possible for harvesters to recover the full email address from the HTML alone, without interpreting the CSS, so this cannot be considered safe.
Breaks usability. The email address can be seen, but not selected. This is very frustrating! Eventually, the user is forced to give up or type out the full email address by hand.
Blocked 100% of 318 spammers
HTML
<span class="email">moc.nesnetromrecneps.liame@ea</span>
CSS
span.email { unicode-bidi: bidi-override; direction: rtl; }
Browser
moc.nesnetromrecneps.liame@ea
This technique breaks usability, and can be undone by basic harvesters that don’t interpret CSS, so it is useless.
Breaks usability. The email address can be copied, but the text is reversed. Eventually, the user is forced to give up or type out the full email address by hand.
These techniques protect a clickable link that will open the user’s mail client (e.g. email). Note that only the href “mailto:” attribute is protected. If the link text also contains the email address, then the email address is additionally exposed as plain text, and you’ll need to layer on at least one of the plain-text obfuscation techniques.
Blocked 0% of 299 spammers
HTML
<a href="mailto:am@email.spencermortensen.com">email</a>
Browser
Blocked 100% of 299 spammers
HTML
<a href="mailto:an@email.spencermortensen.com">email</a>
Browser
HTML entities are often decoded automatically by server-side libraries, which means that even the most basic harvesters can get your email addresses without any special effort. This technique should be worthless—and, yet, it still stops most harvesters.
Blocked 96% of 299 spammers
HTML
<a href="mailto:%61%6f%40%65%6d%61%69%6c%2e%73%70%65%6e%63%65%72%6d%6f%72%74%65%6e%73%65%6e%2e%63%6f%6d">email</a>
Browser
Server-side libraries make it trivial to undo URL encoding, so this technique is essentially worthless—and, yet, it still stops most harvesters.
Blocked 100% of 299 spammers
HTML
<a rel="nofollow, noindex" href="email/">email</a>
.htaccess
RewriteEngine On RewriteRule ^email/$ 'mailto:email@example.com' [R=302,L]
.htaccess
RewriteEngine On RewriteRule ^email/$ 'mailto:email@example.com?subject=Hi' [R=302,QSA,L]
Browser
This technique turns a “mailto:” link into a regular link, without breaking its mail capability, and hides it among the other links on the page.
If you’re planning to fill out any of the fields of the email, then you should use the second form of the “.htaccess” file (with the QSA flag included) to ensure that the query string is preserved.
Because the link doesn’t lead to an actual webpage, search engines might report this to you as a broken link. The “nofollow, noindex” tags prevent this by instructing search engines not to follow and index the link.
When you’re fully satisfied with the redirect, you may wish to switch from a temporary (302) redirect to a permanent (301) redirect. After you’ve loaded a permanent (301) redirect, your browser will ignore any further changes that you make. This makes further testing more difficult, but it can make the redirect faster.
Blocked 100% of 299 spammers
HTML
<object class="email" width="33" height="24" data="email.svg" type="image/svg+xml"></object>
SVG: email.svg
<svg viewBox="0 0 33 24" xmlns="http://www.w3.org/2000/svg"> <style> @import url('https://fonts.googleapis.com/css2?family=Indie+Flower&display=swap'); text { dominant-baseline: middle; fill: #000; font-family: 'Indie Flower'; font-size: 16px; text-anchor: middle; } </style> <a href="mailto:email@example.com"> <text x="50%" y="50%">email</text> </a> </svg>
CSS
object.email { height: 2em; margin: -1em 0; vertical-align: middle; }
Browser
Inspiration: rouninmedia.github.io
This hides the email address in an unusual place where most harvesters won’t think to look. However, the email address is stored there in plain text.
This technique is accessible to all users, including those who depend on a screen reader. But you must use an object element for this to work: an img element would give you an image that is non-interactive, and inline SVG would put the email address in the source code where harvesters would find it easily.
The “width” and the “height” attributes help prevent layout shifts while the page is loading. Because the dimensions depend on the font, the SVG file has to explicitly specify a web font to prevent rendering issues.
Blocked 100% of 299 spammers
HTML
<script>document.write('<a href="mailto:'+'a'+'p'+'@'+'e'+'m'+'a'+'i'+'l'+'.'+'s'+'p'+'e'+'n'+'c'+'e'+'r'+'m'+'o'+'r'+'t'+'e'+'n'+'s'+'e'+'n'+'.'+'c'+'o'+'m'+'">email</a>');</script>
Browser
This is convenient because it has no external dependencies, and yet still manages to block most harvesters. However, the full email address appears directly in the HTML, so this technique cannot be considered safe.
Blocked 100% of 299 spammers
HTML
<a class="email" href="znvygb:nd@rznvy.fcraprezbegrafra.pbz">email</a>
HTML
<head> <script src="[link-rot18.js](https://spencermortensen.com/articles/email-obfuscation/files/link-rot18.js)" defer></script> </head>
Browser
This technique can be undone by basic harvesters that don’t interpret JavaScript. At the very least, you should rotate your letters by something other than 13, and rotate your numbers by something other than 5.
Blocked 100% of 299 spammers
HTML
<a id="link-conversion" rel="nofollow, noindex" href="to-email-spencer/">email</a>
HTML
<head> <script src="[link-conversion.js](https://spencermortensen.com/articles/email-obfuscation/files/link-conversion.js)" defer></script> </head>
Browser
In this technique, the HTML source code contains a decoy link, and you write a custom function that converts that decoy link into a working “mailto” link.
Most harvesters can only access the HTML source code—and the source code contains nothing of value. The only practical way to restore the “mailto” link is to run your custom conversion function in a web client with DOM and JavaScript support. This is not possible for most harvesters.
You can write a custom function for each email address, or you can write a single function and apply it to all of the email addresses on the page.
Despite being frighteningly simple, this is expected to be one of the very best techniques.
Blocked 100% of 299 spammers
HTML
<a class="email" rel="nofollow, noindex" href="xd7L-AOA9Qckl5RXbFuFw8LxuiPZPk3vzyPS55-KlD-a78c00rng">email</a>
HTML
<head> <script src="[link-aes.js](https://spencermortensen.com/articles/email-obfuscation/files/link-aes.js)" defer></script> </head>
Browser
This technique uses AES 256 to encrypt the email address. (AES is the only publicly-available cipher approved by the NSA for top secret information.) The email address cannot be recovered without the JavaScript file—which most harvesters cannot access or run. This implementation uses the browser’s own built-in cryptography library, so it will not run outside of the browser, even in JavaScript-capable environments.
The cryptography library, SubtleCrypto, is only available in secure contexts (such as over https or on localhost)—which could be a blocker if you’re using http. You must upgrade to https before you can use this technique!
Blocked 100% of 299 spammers
HTML
<a id="link-interaction" rel="nofollow, noindex" href="to-email-spencer/">email</a>
HTML
<head> <script src="[link-interaction.js](https://spencermortensen.com/articles/email-obfuscation/files/link-interaction.js)" defer></script> </head>
Browser
This technique keeps the email address hidden until the user interacts with the page; only then is the email address revealed. This raises the bar for harvesters: they not only need to run a full web client, they also need to interact with it.
This technique can be used to trigger other techniques.
Every obfuscation technique can be broken, so some people assume that there is no point to obfuscation. Nothing could be further from the truth! Most harvesters are unsophisticated, and even the simplest techniques are unreasonably effective at defeating them.
Harvesters often jump straight to the high-traffic pages, ignoring low-traffic pages and links. As a result, some pages are very rarely or never seen by harvesters, which can lead people to falsely believe that no pages ever need protection. This is false, and dangerous, since a page can suddenly go viral.
This article is not only an article for human readers, it’s also a honeypot for harvesters. Each technique is protecting an email address: When that address receives spam, I know which technique was broken. I make a list of which addresses each spammer knows, and I use that list to build up the statistics.
In order to reliably collect statistics, I’ve had to disable spam filtering at every step along the way. The big mail providers filter out a huge amount amount of spam that never reaches their users (some of it appears in the Spam folder, and some of it is silently deleted and never shown). Desktop mail clients often do something similar. As a result, I’ve set up my own mail server and client.
I deduplicate messages as best I can, so the statistics won’t be distorted by arbitrary details like the volume of email that a spammer sends. I keep track of which email addresses the spammer knows, and nothing else. This is a lot harder than it looks, because spammers often try to conceal their identities!
Harvesters generally don’t target both plain-text address and clickable links, so I keep those statistics independently: That’s why the total spammer counts are different for text-based and link-based obfuscation techniques.
There is some uncertainty in the statistics because the sample sizes are still relatively small. (Small, but growing.) The more people link to this article, the better these statistics will become!