Small Go client for hCaptcha Siteverify. It verifies a response token on your server after the browser completes hCaptcha; it does not render the widget or make authorization decisions for you.
go get github.com/hCaptcha/hcaptcha-go/hcaptchaRequires Go 1.24 or later. Use a supported, patched Go toolchain in production.
This minimal local server uses HCAPTCHA_SECRET and HCAPTCHA_SITEKEY. Create the verifier at startup and reuse it across handlers. Exit before serving requests if either value is missing.
package main
import (
"log"
"net/http"
"os"
"time"
hcaptcha "github.com/hCaptcha/hcaptcha-go/hcaptcha"
)
func main() {
verifier, err := hcaptcha.New(os.Getenv("HCAPTCHA_SECRET"))
if err != nil {
log.Fatalf("hcaptcha init failed: %v", err)
}
siteKey := os.Getenv("HCAPTCHA_SITEKEY")
if siteKey == "" {
log.Fatal("HCAPTCHA_SITEKEY is required")
}
http.HandleFunc("POST /protected", func(w http.ResponseWriter, r *http.Request) {
protected(verifier, siteKey, w, r)
})
server := &http.Server{Addr: "127.0.0.1:8080", ReadHeaderTimeout: 5 * time.Second}
log.Fatal(server.ListenAndServe())
}
func protected(verifier *hcaptcha.Client, siteKey string, w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
http.Error(w, "invalid form", http.StatusBadRequest)
return
}
token := r.PostForm.Get("h-captcha-response")
if token == "" {
http.Error(w, "token is required", http.StatusBadRequest)
return
}
result, err := verifier.VerifyRequest(r.Context(), hcaptcha.Request{
Token: token,
SiteKey: siteKey,
})
if err != nil {
http.Error(w, "verification unavailable", http.StatusBadGateway)
return
}
if result["success"] != true {
http.Error(w, "verification failed", http.StatusForbidden)
return
}
// Perform the protected action.
}Use Verify(token) for the minimal case. VerifyContext(ctx, token) and VerifyRequest(ctx, request) propagate handler cancellation. Request.Token is required; RemoteIP and SiteKey are optional parameters sent only when non-empty.
New uses a one-second HTTP timeout per attempt; use NewWithHTTPClient to configure it. Request.MaxRetries sets extra attempts after transport errors or HTTP 429/5xx. The default 0 makes one attempt; 2 allows three. Retry delays start at 100 ms, double up to 1 s, and stop on context cancellation.
Production tokens are single-use. The first POST may succeed at hCaptcha even if your server times out before receiving the reply. Retrying the same token can then return already-seen-response; treat it as failure and obtain a fresh token.
Result decodes the Siteverify JSON object into map[string]any. VerifyRequest returns an error for non-2xx HTTP status, transport failure, or invalid JSON. A well-formed 2xx response with success: false has no Go error. Deny the protected action unless result["success"] == true. Inspect error-codes for operational logging; do not expose raw verification details to users.
This package receives the token generated by a browser or mobile integration. Submit the widget's h-captcha-response field to your backend, then call this package from that backend. For the standalone server above, post to /protected and use the value of HCAPTCHA_SITEKEY as the widget sitekey. The runnable React/Vite example below uses VITE_HCAPTCHA_SITEKEY and posts to /api/protected, which Vite proxies to the backend's /protected route.
Use the Developer Guide for the complete browser-to-server flow and the JavaScript configuration reference for explicit rendering, callbacks, themes, and client-side errors.
- Call Siteverify from the backend with a form-encoded POST. Do not expose the secret or accept a client-side success claim.
- Verify tokens promptly. hCaptcha tokens are short-lived and single-use.
- Pass the expected
SiteKeyto prevent a token issued for another sitekey from being accepted.hostnameis informational, not an authentication control. - Send
RemoteIPwhen available, using an IP derived from a trusted-proxy policy. Do not trust forwarding headers from arbitrary clients. - Enable the hCaptcha domain allowlist when available. Keep the secret in a secret manager or environment variable; rotate a leaked secret immediately.
See hCaptcha’s server-side verification guidance, domain allowlist configuration, and Siteverify error-code reference.
From the repository root, run package tests:
go test ./...Run the included React/Vite browser and Go backend example:
cd example
cp -n .env.local.example .env.local
npm ci
./run.shThe sample env uses hCaptcha's public test keys, which skip visual challenges. For real keys, follow the example's development hostname setup; hCaptcha does not support localhost or 127.0.0.1 as hostnames.