Spammers are a huge deal nowadays. If you want to share your contact information without getting overwhelmed by spam email you need a solution. I run into this problem a few months ago. While I was researching how to solve it, I found different interesting solutions. Only one of them was perfect for my needs.
In this article, I am going to show you how to easily protect your email address from spam bots with multiple solutions. It’s up to you to decide what technique fits your needs.
The traditional case
Let’s say that you have a website. You want to share your contact details, and you don’t want to share only your social links. The email address must be there. Easy, right? You type something like this:
<a href="mailto:[email protected]">Send me an Email</a>
And then you style it according to your tastes.
Well, even if this solution works, it has a problem. It makes your email address available to literally everyone, including website crawlers and all sorts of spam bots. This means that your inbox can be flooded with tons of unwanted rubbish like promotional offers or even some phishing campaign.
We are looking for a compromise. We want to make it hard for bots to get our email addresses, but as simple as possible for normal users.
The solution is obfuscation.
Obfuscation is the practice of making something difficult to understand. This strategy is used with source code for multiple reasons. One of them is hiding the purpose of the source code to make tampering or reverse-engineering more difficult. We will first look at different solutions that are all based on the idea of obfuscation.
The HTML approach
We can think of bots as software that browse the web and crawl through web pages. Once a bot obtains an HTML document, it interprets the content in it and extracts information. This extraction process is called web scraping. If a bot is looking for a pattern that matches the email format, we can try to disguise it by using a different format. For example, we could use HTML comments:
<p>If you want to get in touch, please drop me an email at<!-- fhetydagzzzgjds --> email@<!-- sdfjsdhfkjypcs -->addr<!-- asjoxp -->ess.com</p>
It looks messy, but the user will see the email address like this:
If you want to get in touch, please drop me an email at [email protected]
Pros:
- Easy to set up.
- It works with JavaScript disabled.
- It can be read by assistive technology.
Cons:
- Spam bots can skip known sequences like comments.
- It doesn’t work with a
mailto:
link.
The HTML & CSS approach
What if we use the styling power of CSS to remove some content placed only to fool spam bots? Let’s say that we have the same content as before, but this time we place a span
element inside:
<p>If you want to get in touch, please drop me an email at <span class="blockspam" aria-hidden="true">PLEASE GO AWAY!</span> email@<!-- sdfjsdhfkjypcs -->address.com</p>.
Then, we use the following CSS style rule:
span.blockspam {
display: none;
}
The final user will only see this:
If you want to get in touch, please drop me an email at [email protected].
…which is the content we truly care about.
Pros:
- It works with JavaScript disabled.
- It’s more difficult for bots to get the email address.
- It can be read by assistive technology.
Con:
- It doesn’t work with a
mailto:
link.
The JavaScript approach
In this example, we use JavaScript to make our email address unreadable. Then, when the page is loaded, JavaScript makes the email address readable again. This way, our users can get the email address.
The easiest solution uses the Base64 encoding algorithm to decode the email address. First, we need to encode the email address in Base64. We can use some websites like Base64Encode.org to do this. Type in your email address like this:
Then, click the button to encode. With these few lines of JavaScript we decode the email address and set the href
attribute in the HTML link:
var encEmail = "ZW1haWxAYWRkcmVzcy5jb20=";
const form = document.getElementById("contact");
form.setAttribute("href", "mailto:".concat(atob(encEmail)));
Then we have to make sure the email link includes id="contact"
in the markup, like this:
<a id="contact" href="">Send me an Email</a>
We are using the atob
method to decode a string of Base64-encoded data. An alternative is to use some basic encryption algorithm like the Caesar cipher, which is fairly straightforward to implement in JavaScript.
Pros:
- It’s more complicated for bots to get the email address, especially if you use an encryption algorithm.
- It works with a
mailto:
link. - It can be read by assistive technology.
Con:
- JavaScript must be enabled on the browser, otherwise, the link will be empty.
The embedded form approach
Contact forms are everywhere. You certainly have used one of them at least once. If you want a way for people to directly contact you, one of the possible solutions is implementing a contact form service on your website.
Formspree is one example of service which provides you all the benefits of a contact form without worrying about server-side code. Wufoo is too. In fact, here is a bunch you can consider for handling contact form submissions for you.
The first step to using any form service is to sign up and create an account. Pricing varies, of course, as do the features offered between services. But one thing most of them do is provide you with an HTML snippet to embed a form you create into any website or app. Here’s an example I pulled straight from a form I created in my Formspring account
<form action="https://formspree.io/f/[my-key]" method="POST">
<label> Your email:
<input type="email" name="email" />
</label>
<label> Your message:
<textarea name="message"></textarea>
</label>
<!-- honeypot spam filtering -->
<input type="text" name="_gotcha" style="display:none" />
<button type="submit">Send</button>
</form>
In the first line, you should customize action
based on your endpoint. This form quite basic, but you can add as many fields as you wish.
Notice the hidden input tag on line 9. This input tag helps you filter the submissions made by regular users and bots. In fact, if Formspree’s back-end sees a submission with that input filled, it will discard it. A regular user wouldn’t do that, so it must be a bot.
Pros:
- Your email address is safe since it is not public.
- It works with Javascript disabled.
Con:
- Relies on a third-party service (which may be a pro, depending on your needs)
There is one other disadvantage to this solution but I left it out of the list since it’s quite subjective and it depends on your use case. With this solution, you are not sharing your email address. You are giving people a way to contact you. What if people want to email you? What if people are looking for your email address, and they don’t want a contact form? A contact form may be a heavy-handed solution in that sort of situation.
Conclusion
We reached the end! In this tutorial, we talked about different solutions to the problem of online email sharing. We walked through different ideas, involving HTML code, JavaScript and even some online services like Formspree to build contact forms. At the end of this tutorial, you should be aware of all the pros and cons of the strategies shown. Now, it’s up to you to pick up the most suitable one for the your specific use case.
Hi, I think the HTML/CSS example should have looked like this:
If you want to get in touch, please drop me an email at email@<span>PLEASE GO AWAY!</span>address.com.
The way it’s currently written – with a comment – has no effect different from the HTML only approach.
If you’re using WordPress, it has a very handy antispambot function:
Wow, thanks for sharing!
How about using the native form action?
And to obfuscate for bots:
Pros
Works with native form elements
No additional javascript files
Cons
Can’t right click and copy email address
There is still javascript but it’s in the HTML
Regular HTML works perfectly with JS being as it works in mailto links. I’ve used this for decades and it is tried and true method I have carried for 20+ years now.
Or, combine multiple solutions. Use HTML + CSS to block HTML scanners:
And then use JavaScript to wrap the address in an
<a>
with amailto:
attribute.Does someone knows from experience the effectiveness of each approach?
I remember setting a comment form a long time, something like the embedded form approach, using a honeypot input, and still there were bots were setup to ignore the honeypot and sent spam using that form. More recently I made a comment form where the form itself was rendered using JavaScript: it still got a spam message – well, a single one, maybe it was a human spammer, not a bot.
A 3td party service like https://pageclip.co/ is also good in a pinch, also connects to slack
There are also services like mailhide.io that put a captcha in front of the email address
I can confirm that a hidden honeypot input in a contact form doesn’t reliably filter bots. I tried that 15 years ago and even back then bots were already too clever for that.
Guys, this is css tricks right? Why not this:
span.email::after {content:”@domain.com”}
It’s not accessible for screenreaders
What about then using aria-label to add the last @domain.com bit?
The HTML & CSS approach will not work in TUI browsers.
I have heard that changing the at symbol (@) to @ and period (.) to . works, like the following example. I have been using this approach, and it seems to work.
Pssst – don’t tell. Spambot could be updated to recognise Entities! Up to now (for simplicity) they only scan for the @-sign.
Why does the author use input nested in label? Label has a for attribute
Am I the only one using an image eith my email in the jpg or gif?
For, though, requires an id vs wrapping an input with label so you don’t need to manage ids and you get the same benefits. Since ids need to be unique (outside of say, web component or iframes), it saves time and effort. I personally feel it’s superior but that’s subjective
Best solution have two emails. A ghost email where you can check for the ultra-rare occasion of someone with javascript disabled.
And the real one, that gets written over the ghost one after page loads with javascript (only 3 lines of code).
Very, very simple + effective.
Pros
Just Everything.
Cons
2 email address.
i use:
.e-mail:before {
content: attr(data-website) “\0040” attr(data-user);
unicode-bidi: bidi-override;
direction: rtl;
}
I really like this. Good work.
How can I make that clickable to start default mail client, ideally w/o javascript?
<a class=e-mail data-user="resu" data-website="moc.niamod" href="mailto://"></a>
ok, I did this:
<script defer>
document.querySelectorAll('.e-mail').forEach(e => {
e.addEventListener("click", event => {
// prevent href=#
event.preventDefault();
// get the (reversed) email address of the calling anchor
z=event.currentTarget;
y=getComputedStyle(z,'::after');
x=y.getPropertyValue('content');
// reverse string rtl
v=x.split("").reverse().join("");
// remove all ""
v=v.replace(/['"]+/g, '');
// start default e-mail client (also test on mobile!)
window.location.href="mailto://"+v;
})
});
</script>
Thank you very much for this useful article. Could it be possible to have the same for phone number and SMS ? Thanks.
Surprised you didn’t mention escaped characters in the first point (the ones starting with
&
), that will work in the mailto link as well (you can even obfuscate the protocol itself this way). In theory this won’t fool any bots that process the DOM properly, but I do wonder how much it culls down the garbage (especially as it hides the@
).Realistically the form approach is probably the best. Make sure to send back a confirmation email, if it doesn’t work then you can assume it’s trash without even having to scan it for spam (while legitimate users will just think that it’s to let them know that you received their message).
Do you really need a third party service to use forms, however? While it’s true that it’s hard to get right (this is where a plug-in may come in handy), pretty sure it should be feasible to do entirely on the site’s server already, especially if the email address is a local one.
I guess the advantage of a third party service is that you don’t reveal any of your real addresses even during the conversation (the user sees the third party’s), but I can see how the user seeing a third party’s address can come off a shady from their viewpoint, and in practice all you really need is a way to know what emails to discard even if they reach you.
I use a JS method where I put some code in the header to combine the email and domain and .com but bots will just see JS code. Totally confusing for bots but selectable and works well
No matter what you use: anything that renders a readable e-mail address to the visitor will also render it readable for scrapers that use the rendered output instead of the source code, or (even harder to beat) use OCR.
Bill Bontrager has a free spam-free form maker here: https://spamfreeform.com/
This is a great article – thanks Lorenzo! Concerns like the need to protect email addresses never entirely disappear and it’s helpful every so often to review what technologies and techniques we have in our toolset.
When reading the article I saw there was no mention of SVG. It occurred to me that an SVG-based approach was an approach worth exploring – it would successfully remove any javascript requirement while also allowing for normal
mailto:
links.In short, it’s possible to write an SVG which contains:
and then embed that SVG in HTML via:
I have a full write-up here:
https://github.com/RouninMedia/protecting-your-email-address-via-svg-instead-of-js/blob/main/README.md
I wrote a web component for this purpose, and the nice benefit is that the display email address is in the Shadow DOM (closed) so it can’t be readily inspected by email harvesters that run a browser (which will circumvent many of the tricks shown here); and the click handler is in javascript eg
The shadow DOM code:
Thank you for the article! The article and the comments have a bunch of good ideas.
Nice idea!
Do you know if it actually works against bots?
Thanks for the article Lorenzo
I also solved it with a bit of javascript as well as a bit of UTF-8 encoding for display:
email @ address . com
Since it decodes the string in the comment, for display I use the UTF-8 code 64 for the @ and the code 46 for the dot and put blanks in between so that the email looks like above but a regex parser would fail to recognize it.
I used to have the email address encoded with rot13, and 1 line of javascript to decode on mouse hover/click. very simple and effective. Can’t find it now, but a simple google search will.
Display an address that is correctly formed but miss-spelt so the scraper gets a ‘good’ address and does not look any further. Then at onclick call a short script to correct the spelling error.
This works fine for my site.
For the Javascript version, it seems like it would be helpful to offer a button/link to “Copy Email Address” for people (like me) who never bothered to set up a default mail client. It would be an easy add using navigator.clipboard.writeText()
The level of spam emails is getting way too much! They even manage to beat Gmail spam filters and land inbox!
I just feel spammers will always find a way to spam no matter how careful you are.
I came here trying to see if there’s a modern solution, but I’m surprised it’s still the same as 20 years ago (I’m getting old ).
There’s no doubt that bots can parse JavaScript and HTML encoded characters now… maybe not all of them, but probably a lot more now, especially with the importance of JavaScript crawlers and client-side code being almost fully parsed by Google’s own bots.
So what would be the best solution? Would an initial ajax call, only triggered once user interaction is detected (keyboard event, click event, scroll event, focus events, visibilitychange event) with crsf code / nonce to validate the request does come from the intended website… is it overkill?
no javascript –
maybe i am wrong – but i normaly use this:
in HTML:
<a href="#" class="cryptedmail" data-davor="stuttgart43" data-wohin="juweliere-kraemer" data-ende="de" ></a>
in css
.cryptedmail:after {
content: attr(data-davor) "@" attr(data-wohin) "." attr(data-ende);
}
Flashback to 15 (!) years ago: Graceful E-Mail Obfuscation (A List Apart). By yours truly :)