blog

Email obfuscation

Email obfuscation is a way to prevent various bots and crawlers from collecting email addresses from your website and using them to send spam. Mailtrap has a good article on this subject with a roundup of various techniques.

The Cloudflare way

My email on this blog is obfuscated by Cloudflare. When serving my website from CDN, they replace the email address I added in HTML with a string that looks like this:

<a href="/cdn-cgi/l/email-protection#4430212b6a203625232b322d27042329252d286a272b29" aria-label="Contact me">

And they inject small piece of JavaScript that converts it back into readable, clickable mailto link for visitors. I like it since I don’t have to change or do anything special on my end. It’s a one-click option to turn on in Cloudflare settings. And it’s available on their free tier!

The DIY way

The very first iterations of Hugo website featured "about us" section where each team member had their email listed. There I couldn’t use Cloudflare so I did my own obfuscation using some CSS, JS and Nunjucks magic.

First, the HTML/Nunjucks code:

<span class="visually-hidden">{{ email | replace( '@', ' at ' ) }}</span>
<a class="mail js-mail" aria-hidden>{{ email | reverse }}</a>

The first line is intended for assistive technologies. It’s visually hidden but gets read by screen readers. I use Nunjucks native replace filter to both obfuscate and split the email into human-readable parts.

The second line is an anchor for display. It will be hidden from screen readers via aria-hidden tag. Note that it doesn’t have href attribute and email is rendered in reverse (so [email protected] would be displayed as moc.emosewa@olleh).

To make it display normally on screen, I use this bit of CSS:

.mail {
unicode-bidi: bidi-override;
direction: rtl;
}

By combining unicode-bidi: bidi-override and direction: rtl mail address is reversed back to original. That solves the rendering but clicking the link wouldn’t do anything without the href. To solve that part, we need a bit of JavaScript:

function addMailToHref(selector) {
const links = document.querySelectorAll(selector);

links.forEach(link => {
link.addEventListener('click', event => {
const element = event.target;

if (element.href === '') {
const mailAddress = element.innerText.split( '' ).reverse().join( '' );
return element.setAttribute('href', `mailto:${mailAddress}`);
}
}, { once : true });
});
}

addMailToHref('.js-mail');

This function finds all links with .js-mail class attached and adds a listener on each one so when clicking the link, inner text gets reversed and set as href value with mailto scheme. Returning this will automatically open default email program.

The { once : true } part is addEventListener option to make sure callback is only called on the first click. If a user clicks the same email link again, proper href would already exist and native behavior kicks in.

What’s missing from this approach is a fallback when JavaScript is disabled. In that case, doing copy/paste of the email will get you a reversed value. One solution is to use .no-js class and display visually hidden mail instead. Or use email encoder.