Introduction
Publishing a plain mailto: address invites spam bots to harvest it. This tutorial shows three simple ways to reduce spam on a Jekyll site while keeping the link usable for visitors and screen readers. You can mix and match based on your needs.
Overview of approaches
| Method | Pros | Cons | Best for |
|---|---|---|---|
| JavaScript-built link | Good balance of usability and protection; easy to reuse | Requires JavaScript; provide a fallback | Most sites |
| HTML entity encoding | No JavaScript; very simple | Some bots decode entities | Basic protection |
| Contact page | Hides email entirely; can add rate limiting and spam filters | Needs a backend or service | High-traffic or privacy-focused sites |
Method 1: JavaScript-built mail link (recommended)
What it does: Stores the parts of your email in data attributes. A small script assembles a working mailto: link at runtime. Bots that only scan static HTML see no email address. Users without JavaScript see a readable fallback.
Step A — Create an include for the link
What it does: Adds a reusable Liquid include you can drop anywhere in your site.
{% raw %}{% comment %}
_includes/email-link.html
Usage:
{% include email-link.html user="contact" domain="example" tld="org" label="Email us" subject="Hello" %}
{% endcomment %}
{{ include.label | default: "Email" }}
{% endraw %}
Step B — Add a lightweight script
What it does: Finds links with .js-email and converts them into real mailto: links.
document.addEventListener("DOMContentLoaded", function () {
var links = document.querySelectorAll(".js-email");
links.forEach(function (el) {
var user = el.getAttribute("data-user") || "";
var domain = el.getAttribute("data-domain") || "";
var tld = el.getAttribute("data-tld") || "";
var subject = el.getAttribute("data-subject");
var address = user + "@" + domain + "." + tld;
var href = "mailto:" + address + (subject ? ("?subject=" + subject) : "");
el.setAttribute("href", href);
el.setAttribute("aria-label", address);
});
});
Save the script as assets/email.js and include it in your layout (before </body>):
<script src="/assets/email.js" defer></script>
Step C — Use the include
What it does: Renders a link that becomes a mailto: at runtime, with a no-JS fallback.
{% raw %}{% include email-link.html user="contact" domain="example" tld="org" label="Email us" subject="Inquiry from website" %}{% endraw %}
Method 2: HTML entity encoding
What it does: Converts each character of the address into a numeric HTML entity. Browsers render it as normal text, but simple scrapers may miss it.
Step A — Generate an encoded address
Run this one-liner and replace the email with yours. It prints an encoded string you can paste in HTML.
python3 - <<'PY'
s = "contact@example.org"
print(''.join('{};'.format(ord(c)) for c in s))
PY
Step B — Use it in HTML
What it does: Displays a working link; the mailto: URL can also be entity-encoded.
<a href="mailto:contact@example.org" rel="noopener">
contact@example.org
</a>
Note: Some advanced bots decode entities, so combine this with extra measures if spam is severe.
Method 3: Contact page instead of an email address
What it does: Avoids publishing an email address. Use a contact form that sends messages to you. This reduces scraping and allows rate limiting and spam filtering.
If you self-host a backend, link to a contact page:
<p>Prefer a contact form? <a href="/contact/">Send a message here</a>.</p>
Privacy note: Choose a backend that fits your privacy and legal requirements. If you cannot run a backend, consider a form service with strong privacy guarantees and a data processing agreement.
Tips and notes
- Use an email alias dedicated to your site. You can rotate it if spam increases.
- Add DNS-level spam filtering and server-side rules. Client-side obfuscation is only one layer.
- Provide a no-JavaScript fallback for accessibility and users on restricted networks.
- Do not expose the address in
alttext or filenames; bots scan those too.
Troubleshooting
The link does nothing when clicked.
Ensure the JavaScript file is loaded with the correct path and that you used the .js-email class.
Users without JavaScript cannot email.
Keep the <noscript> fallback visible, or add a contact page link near the email.
The encoded string shows codes instead of characters.
Make sure you pasted the output exactly and did not escape the ampersands (use &#... only inside the attribute or text, not &#...).
Spam persists.
No single method is perfect. Combine methods: JavaScript assembly, entity encoding, a contact page, and an alias with server-side filtering.
Resources and further reading
- Jekyll Documentation — https://jekyllrb.com/docs/
- MDN: Using mailto links — MDN mailto
- OWASP: Automated threats and scraping — general guidance on bot risks