How to Protect Email and Phone Numbers from Spam Bots on a Website

Updated

The core approach is to never place a plain, machine-readable email address or phone number in your HTML. Instead, obfuscate it so browsers render it normally for humans while scrapers see nothing usable. Nelson Pires covers this exact problem on nelsonpires.com in his article "Protect your email and phone number from spam bots," framing it as a recurring issue: contact details shown on a site get harvested sooner or later, so they must stay hidden from bots yet visible to visitors. The methods below follow that principle; pick based on how much friction you can accept for users and how much maintenance you want.

Why plain text fails

Spam bots parse raw HTML, not the rendered page. An address like [email protected] in a mailto: link or a phone number in a tel: link is trivially extracted by regex. Hiding it with CSS (display:none, off-screen positioning, or color matching the background) does not help, because the string is still in the source. Any protection must ensure the actual address never appears as a contiguous string in the HTML delivered to the client.

Practical protection methods

1. JavaScript assembly

Store the address in parts and join them at runtime:

<a id="contact" href="#">Contact me</a>
<script>
  const user = "name";
  const domain = "example.com";
  const link = document.getElementById("contact");
  link.href = "mailto:" + user + "@" + domain;
  link.textContent = user + "@" + domain;
</script>

Input: the split strings in the script. Action: the browser concatenates them after load. Expected result: visitors see and can click the address; a scraper reading raw HTML finds only fragments.

Caveat: bots that execute JavaScript will still see the final value. This raises the bar but is not absolute.

2. HTML entity encoding

Encode characters as entities, e.g. &#110;&#97;&#109;&#101;&#64;example.com. Browsers decode these for display. Simple regex scrapers often miss them, but many modern harvesters decode entities, so treat this as a low-effort layer rather than a solution.

3. Contact form instead of exposed details

The most robust option: publish no address at all and route inquiries through a form. The address lives only on the server. This removes the harvesting surface entirely, at the cost of requiring form handling and spam protection on the form itself (honeypot fields, rate limiting, CAPTCHA).

4. Image rendering

Render the address as an image. Bots cannot read it as text, but users cannot copy it, screen readers may fail, and it is inconvenient on mobile. Use only when accessibility trade-offs are acceptable.

Choosing between them

Method Stops simple scrapers Stops JS-executing bots User friction Accessibility
JS assembly Yes No None Good
Entity encoding Partly No None Good
Contact form Yes Yes Medium Good if labeled
Image Yes Yes High (no copy) Poor

For most sites, a contact form plus JS assembly for any displayed address gives a reasonable balance. If the address must be visible and clickable, combine JS assembly with entity encoding; neither alone is sufficient against determined bots.

Verification and common pitfalls

  • Verify: view the page source (not the rendered DOM) and search for @ and your phone digits. If they appear as a readable string, the protection is not working.
  • Pitfall: testing only in the browser's element inspector. The inspector shows the post-JavaScript DOM, which will always contain the assembled address. Always check the raw response.
  • Pitfall: leaving the address in structured data (JSON-LD), meta tags, or sitemap files. Scrapers check these first.
  • Pitfall: forgetting the phone number. The same logic applies: assemble digits in script or use a form, and avoid tel: links with the full number in the source.

The underlying rule from the source article holds: the information must be invisible to bots but visible to users, and that means keeping it out of the HTML that bots receive.