How to Obfuscate Email Addresses from Scrapers
The Spam Problem
Put an email address in plain text on a web page and it will be scraped by bots within hours. These bots crawl the web looking for email patterns to add to spam lists. Once your address is on those lists, the spam never stops.
But you still want visitors to be able to contact you. The challenge is making your email accessible to humans while invisible to automated scrapers. Here are the techniques I use on my portfolio site, ordered from simplest to most robust.
Technique 1: JavaScript Assembly
The most effective and practical approach. Bots that scrape emails typically do not execute JavaScript, so assembling the email address in JavaScript hides it from most scrapers:
<span id="email-link"></span>
<script>
(function() {
var user = 'steve';
var domain = 'stevecv.com';
var element = document.getElementById('email-link');
var addr = user + '@' + domain;
element.innerHTML = '<a href="mailto:' + addr + '">' + addr + '</a>';
})();
</script>The email address never appears in the HTML source. It is only constructed when JavaScript executes in a browser. This blocks the vast majority of email scrapers.
Making It Accessible
The basic JavaScript approach has an accessibility issue: if JavaScript fails to load, there is no email at all. Add a fallback:
<span id="email-link">
<noscript>Enable JavaScript to see the email address</noscript>
</span>For screen readers, the dynamically inserted anchor tag works fine. Screen readers execute JavaScript, so they will read the mailto link correctly.
Technique 2: CSS Direction Reversal
This technique writes the email backwards in HTML and uses CSS to reverse the display direction:
<style>
.email-obfuscated {
unicode-bidi: bidi-override;
direction: rtl;
user-select: all;
}
</style>
<span class="email-obfuscated">moc.vcevetc@evetc</span>The HTML source contains the reversed email, which bots cannot parse. CSS reverses it visually for humans. However, this approach has drawbacks: copying the text copies the reversed version, and screen readers may read it backwards. I do not recommend this as a primary technique.
Technique 3: HTML Entity Encoding
Replace characters with HTML entities:
<a href="mailto:
steve@
stevecv.com">
Contact Me</a>Each character is replaced with its decimal HTML entity. Browsers render this correctly, but simple regex-based scrapers will not recognize the pattern. More sophisticated bots can decode entities, so this provides moderate protection.
Generating Encoded Emails with Python
def encode_email(email: str) -> str:
return ''.join(f'&#{ord(c)};' for c in email)
print(encode_email('mailto:steve@stevecv.com'))Technique 4: Contact Form
The most secure approach is to not display the email at all. Replace it with a contact form that sends emails server-side:
<form action="/api/contact" method="POST">
<label for="name">Name</label>
<input type="text" id="name" name="name" required>
<label for="message">Message</label>
<textarea id="message" name="message" required></textarea>
<button type="submit">Send Message</button>
</form>The email address only exists in your server-side code. Bots cannot scrape what is not on the page. The tradeoff is that you need a backend to handle form submissions, and some visitors prefer direct email.
My Recommended Approach
I use a combination of techniques on my portfolio:
- JavaScript assembly for the clickable email link (blocks most bots)
- HTML entity encoding as a fallback in noscript (moderate protection without JS)
- Honeypot field in the contact form (catches bots that submit forms)
<span id="email-container">
<noscript>
<a href="mailto:steve@stevecv.com">
Email Me
</a>
</noscript>
</span>
<script>
(function() {
var p = ['steve', 'stevecv', 'com'];
var a = p[0] + '@' + p[1] + '.' + p[2];
var e = document.getElementById('email-container');
e.innerHTML = '<a href="mailto:' + a + '">' + a + '</a>';
})();
</script>Honeypot Fields for Forms
If you use a contact form, add a hidden field that real users will not fill in but bots will:
<!-- Hidden from visual users with CSS -->
<div style="position: absolute; left: -9999px;">
<label for="website">Website</label>
<input type="text" id="website" name="website" tabindex="-1" autocomplete="off">
</div>On the server side, reject any submission where the honeypot field has a value. This catches automated bots without adding friction for real users.
What Does Not Work
- Replacing @ with [at]: Bots recognize this pattern easily
- Using images for email text: Inaccessible to screen readers and not clickable
- ROT13 encoding: Trivially reversible by any script
No technique is perfect. Determined scrapers running headless browsers will execute JavaScript and see your email. But the goal is not perfection; it is raising the bar high enough to block the vast majority of automated scrapers. The JavaScript assembly technique accomplishes this with minimal effort.