Common honeypot patterns
All of these tricks share one idea: the element exists in the HTML but is hidden from the eye. Here are the classic ones:
<input name="email" type="text" style="display:none">
<input name="email" type="text" style="visibility:hidden">
<input name="email" type="text" style="opacity:0">
<input name="email" type="text" style="width:0; height:0">
<input name="email" type="text" tabindex="-1">
<input name="phone" style="position:absolute; left:-9999px">
<a href="/admin/honeypot" style="display:none">Admin</a>
<!-- after </body> — never rendered, only seen by parsers -->
</body>
<a href="/honeypot-trap">Trap</a>Each line hides the element a different way: display:none removes it from the layout, opacity:0 makes it fully transparent, left:-9999px shoves it far off the left edge of the screen, and tabindex="-1" stops the keyboard from ever reaching it. A bot that fills every input or follows every href triggers the trap. A human never sees these elements at all because the browser does not render them.
Mitigation in practice
The fix is simple: before you touch any element, ask whether a human could actually see it. This helper does exactly that — it checks the on-screen box and the computed style (the final styling the browser applied), and bails out on anything zero-sized, hidden, or transparent:
def is_visible(element):
box = element.bounding_box()
if not box or box["width"] == 0 or box["height"] == 0:
return False
style = element.evaluate("el => getComputedStyle(el)")
if style["display"] == "none": return False
if style["visibility"] == "hidden": return False
if float(style["opacity"]) == 0: return False
return True
for link in page.query_selector_all("a"):
if is_visible(link):
# safe to click
...If you scrape by parsing raw HTML instead of running a real browser (Scrapy, BeautifulSoup), you have no browser to tell you what is visible, so you must apply the same rules by hand: respect inline style="display:none" and the hidden attribute, filter elements positioned off-screen (left: -9999px), and skip anything that appears outside or after the <body>.
Where this is deployed
Honeypots show up most on the pages where automation is expected to attack: login forms, account-registration flows, contact-us forms, and comment sections — anywhere bots try to brute-force credentials or scrape in bulk. Every major anti-bot vendor ships them as a free first line of defence layered on top of fingerprint-based scoring. Because they cost nothing to add, plenty of independent sites that roll their own anti-scraping use them too — which is why they are the failure mode that catches the most "I have a perfect TLS fingerprint, why am I still blocked?" scrapers.
