diff --git a/.env.example b/.env.example
index 99fb2cad686269647582da680dd220e69eab7dd1..ba76ff58b1d5c2efc18274ce16393ec2077fd99d 100644
--- a/.env.example
+++ b/.env.example
@@ -1,4 +1,6 @@
 ALLOWED_ORIGINS=https://hizla.io
 DB=db
 VERBOSE=1
-LISTEN_ADDR=127.0.0.1:3000
\ No newline at end of file
+LISTEN_ADDR=127.0.0.1:3000
+HCAPTCHA_SITE_KEY=unset
+HCAPTCHA_SECRET=unset
\ No newline at end of file
diff --git a/app.go b/app.go
index e1a11d0cc03133547a407cba2ce239706d3d75d0..3fdf149c5aac989c3009b63a914b94938b49fbf1 100644
--- a/app.go
+++ b/app.go
@@ -36,10 +36,8 @@ func serve(sig chan os.Signal, db *leveldb.DB) error {
 	// /register
 	routeRegister(app, db)
 
-	// Graceful shutdown
-	app.Use(func(c fiber.Ctx) error {
-		return c.Next()
-	})
+	// /hcaptcha-site-key
+	routeHCaptchaSiteKey(app)
 
 	// graceful shutdown
 	go func() {
diff --git a/conf.go b/conf.go
index 15d3daeacd17ceaf20eca91e4e91a49109ddbc5f..8790fc9c7861e56b2c21d0f9dd0fa3afacb14766 100644
--- a/conf.go
+++ b/conf.go
@@ -9,6 +9,8 @@ const (
 	dbPath uint8 = iota
 	listenAddr
 	allowedOrigins
+	hCaptchaSecret
+	hCaptchaSiteKey
 	verboseLogging
 	confLen
 )
@@ -18,6 +20,8 @@ var confEnv = [confLen][2]string{
 	{"DB", "db"},
 	{"LISTEN_ADDR", "127.0.0.1:3000"},
 	{"ALLOWED_ORIGINS", "https://hizla.io"},
+	{"HCAPTCHA_SECRET", "unset"},
+	{"HCAPTCHA_SITE_KEY", "unset"},
 	{"VERBOSE", "1"},
 }
 
diff --git a/register.go b/register.go
index 7276a3dfb9a4e07a3b529f6fb7fbdff34a9303ca..84a00f9ebee65d42c25c745fec40244ffdf68966 100644
--- a/register.go
+++ b/register.go
@@ -4,6 +4,7 @@ import (
 	"log"
 	"regexp"
 	"github.com/gofiber/fiber/v3"
+	"github.com/gofiber/contrib/hcaptcha"
 	"github.com/syndtr/goleveldb/leveldb"
 )
 
@@ -13,9 +14,27 @@ type registration struct {
 	Email string `json:"email"`
 }
 
+// Middleware to conditionally apply hCaptcha
+func conditionalCaptcha(captcha fiber.Handler) fiber.Handler {
+	return func(c fiber.Ctx) error {
+		if conf[hCaptchaSecret] == "unset" {
+			if verbose {
+				log.Printf("Captcha bypassed for %q", c.IP())
+			}
+			return c.Next()
+		}
+		return captcha(c)
+	}
+}
+
 // Waitlist registration route
 func routeRegister(app *fiber.App, db *leveldb.DB) {
-	app.Post("/register", func(c fiber.Ctx) error {
+
+	captcha := hcaptcha.New(hcaptcha.Config{
+		SecretKey: conf[hCaptchaSecret],
+	})
+
+	app.Post("/register", conditionalCaptcha(captcha), func(c fiber.Ctx) error {
 		req := new(registration)
 
 		// Parse and validate the request
@@ -66,4 +85,18 @@ func routeRegister(app *fiber.App, db *leveldb.DB) {
 			"message": "Email registered successfully",
 		})
 	})
+}
+
+// Route to expose hCaptcha site key
+func routeHCaptchaSiteKey(app *fiber.App) {
+	app.Get("/hcaptcha-site-key", func(c fiber.Ctx) error {
+		if conf[hCaptchaSiteKey] == "unset" {
+			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
+				"message": "hCaptcha site key not configured",
+			})
+		}
+		return c.JSON(fiber.Map{
+			"hcaptcha_site_key": conf[hCaptchaSiteKey],
+		})
+	})
 }
\ No newline at end of file