Email obfuscation: A simple way that works in 2024

Email obfuscation: A simple way that works in 2024

Spencer Mortensen spent a year testing email obfuscation techniques to find out what works in 20241. One of the (preliminary) findings: instead of using JavaScript, you just need a little HTML and CSS to block 100% of spam.

CSS Display none

This [technique] requires the harvester to interpret style rules contained in a separate stylesheet document. This is impossible for the vast majority of harvesters, which are something less than a full web browser. As a result, this is one of the best obfuscation techniques.

Use the following HTML:

<span class="email">email@example<b>.example</b>.com</span>

And the following CSS:

span.email b {
display: none;
}

It’s important to use “display: none” to hide elements of the email address, because this is fully supported by all screen readers. Visual-only techniques (such as repositioning part of the text off screen) will prevent everyone except your sighted readers from reaching you.

Hugo shortcode

For my Hugo websites, I translated this technique into a Hugo shortcode. Follow along with how I did this.

First, add a default email address to config/_default/params.toml — we will use this as a fallback when no email address is specified:

# defaultEmail
defaultEmail = "email@example.com"

Next, add the CSS — to assets/scss/common/_custom.scss if you’re using Hyas:

span.email b {
display: none;
}

Then, create the shortcode file:

layouts/shortcodes/email.html
{{- /* Set defaults and get args. */}}
{{- $address := index .Params 0 | default site.Params.defaultEmail }}
{{- /* Get parts. */}}
{{- $addressParts := split $address "@" }}
{{- $userName := (index $addressParts 0) }}
{{- $rootDomain := (index $addressParts 1) }}
{{- $rootDomainParts := split $rootDomain "." }}
{{- $domainName := (index $rootDomainParts 0) }}
{{- $topLevelDomain := (index $rootDomainParts 1) }}
{{- /* Render. */}}
<span class="email">
{{- printf "%s@%s<b>.%s</b>.%s" $userName $domainName $domainName $topLevelDomain | safeHTML -}}
</span>

The shortcode gets the email address you provided — using the default email address if you didn’t specify one. Next, it splits the email address in parts — userName, domainName, and topLevelDomain — and renders the HTML.

Now, you can use the shortcode in Markdown — using defaultEmail:

{{< email >}}

Or, by specifying an email address:

{{< email "email@example.com" >}}

Astro component

I also translated this technique into an Astro component for my Astro websites.

First, add a default email address to src/params.ts — we will use this as a fallback when no email address is specified:

export type siteParams = {
defaultEmail: string;
};
const siteParams: siteParams = {
defaultEmail: "email@example.com"
}
export default siteParams;

Next, add the CSS to src/styles/global.css:

span.email b {
display: none;
}

Then, create the component file:

src/components/Email.astro
---
// Set defaults and get args
import siteParams from '../params';
const defaultEmail = siteParams.defaultEmail;
const { address = defaultEmail } = Astro.props;
// Get parts
const addressParts = address.split('@');
const userName = addressParts[0];
const rootDomain = addressParts[1];
const rootDomainParts = rootDomain.split('.');
const domainName = rootDomainParts[0];
const topLevelDomain = rootDomainParts[1];
---
<!-- Render -->
<span class="email">{userName}@{domainName}<b>.{domainName}</b>.{topLevelDomain}</span>

The component gets the email address you provided — using the default email address if you didn’t specify one. Next, it splits the email address in parts — userName, domainName, and topLevelDomain — and renders the HTML.

Now, you can use the component in MDX — using defaultEmail:

import Email from '../components/Email.astro';
<Email />

Or, by specifying an email address:

import Email from '../components/Email.astro';
<Email address="email@example.com" />

Conclusion

Thanks to Spencer Mortensen’s thorough testing of email obfuscation techniques, we know we can use a simple and effective way of keeping email addresses hidden from spam bots but accessible to readers.

Instead of using JavaScript, you just need a little HTML and CSS to block 100% of spam. I’ve translated this concept into a Hugo shortcode and an Astro component — I hope you find it useful!


Footnotes

  1. Email obfuscation: What works in 2024? — Spencer Mortensen

Join my email newsletter

Get all my new posts delivered directly to your inbox. Unsubscribe anytime.