Product updates, guides, and more

Stay up to date with the news and learn how to get the most out of the platform.

How I Stopped Spam Signups with a Custom Honeypot Captcha

How I Stopped Spam Signups with a Custom Honeypot Captcha

Sep 20, 2025 Security 👁️ 7 reads

Last updated: 2026-02-04

Oops!

We all make mistakes. Mine? I deployed a new version of the site and forgot to protect the /register route. Then I went AFK. By the time I checked back, over 250 fake accounts had been created by bots.

This happened while working on StatusPage.me, and it was a good reminder that “small” auth endpoints still need boring, reliable protection.

Related reading: product features, Statuspage alternatives, and the incident comms primer What Is a Status Page?.

The good news? I had already been working on a small invisible captcha library - a honeypot-based solution that’s effortless for users but brutal on bots. I finalized it, integrated it, and released it as gocaptcha.

How do you stop spam signups with a honeypot captcha?

Add a hidden input field that real users never fill, then reject requests where that field is populated. Bots tend to fill every field they can see in the HTML, so the honeypot becomes a low-friction signal. Pair it with basic rate limiting and logging so you can block repeat offenders without hurting legitimate signups. The result is a simple control that costs almost nothing in UX.

What I did

  • I added a simple hidden input field to the registration form. It’s named something like nickname and styled so that users never see it.
  • On form submission, the server checks whether the field is filled. If it is, the request is likely from a bot and is rejected.
  • I wrapped the logic in a clean Gin middleware and released it as gocaptcha - so any Gin-based app can plug it in with minimal effort.

Why honeypots work 🍯

Spam bots typically fill every field they find in a form. By introducing a field that legitimate users will never touch - but bots will - we can confidently detect automated signups with virtually no user friction.

It’s simple, elegant, and surprisingly effective.

The results

After deploying the honeypot middleware in my Gin app, the effect was immediate.

Spam signups dropped from hundreds to zero - instantly.

Hehe

💡 Here’s a glimpse from the admin dashboard, showing the captcha logs and the now-empty /register route:

Captcha logs

How you can use it

1. Add the hidden input to your form

<form action="/register" method="post">
  <input type="text" name="nickname" style="position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden;" autocomplete="off">
  <!-- visible fields here -->
</form>

2. Validate on the server using gocaptcha

import (
  "github.com/gin-gonic/gin"
  "github.com/dragstor/gocaptcha"
)

r := gin.Default()

cap := gocaptcha.New(gocaptcha.Config{
    ShowBadge:      true,                     // show small lock badge (optional)
    BadgeMessage:   "Protected by GoCaptcha", // badge text
    RateLimitTTL:   time.Minute,              // per-IP window
    RateLimitMax:   10,                       // max requests/window
    EnableStorage:  true,                     // enable SQLite logs + seeding
    DBPath:         "./captcha.db",           // defaults to ./captcha.db if empty
    BlockThreshold: -5,                       // block when score <= threshold
    // Bypass OAuth callbacks:
    SkipPaths:         []string{"/auth/", "/oauth2/"},
    TrustProxyHeaders: true, // respect X-Forwarded-For when behind a proxy (I use Caddy reverse proxy)
})

r.POST("/register", func(c *gin.Context) {
    if cap.CheckRequest(c.Request) {
		// redirect to a login page if bot detected, making them feel like they succeeded
		c.Redirect(http.StatusSeeOther, mainDomain+"/user/login")
		return
	}
  // If middleware passes, continue with registration logic
})

⚠️ A few notes

  • Honeypots are simple and highly effective, but they’re not bulletproof. For maximum protection, combine them with rate limiting, IP filters, and email verification.
  • Choose a believable name for the hidden field - something like "nickname" or "middle_name". Avoid anything obviously fake.
  • Don’t rely on JavaScript alone to hide the field. Use CSS, so non-JS bots still fall into the trap.

Want to try it yourself?

Grab gocaptcha from GitHub: https://github.com/dragstor/gocaptcha

It’s lightweight, Gin-native, and it completely solved my bot problem.

If you also care about keeping public pages privacy-friendly, see: Privacy-First Web Analytics for Status Pages


Tags: captcha, spam, honeypot, gin

Want to see it in action? Head to the live demo:

Protected by gocaptcha


FAQ

Are honeypots enough on their own?

They are a strong first line, but the best setup combines honeypots with rate limiting, email verification, and sane logging.

Will this block real users?

Not if implemented correctly. Real users never see or interact with the honeypot field.

Do I need JavaScript for a honeypot?

No. A server-validated honeypot works without JavaScript and still catches many bots.

Why redirect bots instead of returning an error?

Redirecting to a login page can reduce bot retries by making them “feel” successful.

Author avatar
Nikola Stojković
Published Sep 20, 2025
Related Posts

No related posts were found

Share & Subscribe