How do you integrate hCaptcha with Go?#
Render the hCaptcha widget in your form, read h-captcha-response in the Go handler, and verify the token with the official hCaptcha Go client before the protected action runs. The client sends a form-encoded POST to https://api.hcaptcha.com/siteverify and returns the verification result to your application.
The Go client catalog entry links this integration. Your application renders the widget and decides whether to allow the protected action.
Reduce interruptions on forms verified by Go#
- Keep visitors focused on their task. hCaptcha Pro's 99.9% Passive mode challenges fewer than 0.1% of legitimate users, reducing interruptions on browser forms protected by your Go handler.
- Apply verification in proportion to risk. Pro increases challenge difficulty for suspicious interactions, helping ordinary visitors complete their forms with less friction while retaining stronger checks against abuse.
New Pro sitekeys use 99.9% Passive by default. For an existing sitekey upgraded to Pro, select that mode under Behavior in the hCaptcha dashboard.
Before you start#
You need:
- Go 1.24 or later, using a supported, patched toolchain in production.
- A Go POST handler and HTML form to protect.
- An hCaptcha account with a sitekey and matching secret.
- Outbound HTTPS access from the Go service to hCaptcha.
The client API and module requirements were reviewed on October 29, 2026. Review the official repository and server verification contract when updating the dependency.
Create your hCaptcha credentials#
- Start with hCaptcha Pro for fewer challenges and adaptive protection on forms verified by your Go handler, or use existing compatible hCaptcha credentials.
- Create a sitekey and allow every hostname that will render the widget.
- Store the secret in the deployment environment or secret manager as
HCAPTCHA_SECRET. - Set
HCAPTCHA_SITEKEYand pass this public value to the HTML template.
Never place the secret in HTML, browser JavaScript, committed configuration, logs, or error responses.
Install the official Go client#
Run this command from your application's Go module:
go get github.com/hCaptcha/hcaptcha-go/hcaptcha
Commit the resulting go.mod and go.sum changes so deployments use the reviewed dependency version.
Add the widget to the Go template#
Place the widget inside the protected form and preserve the application's existing CSRF protection:
<form method="post" action="/signup">
<input type="hidden" name="csrf_token" value="{{ .CSRFToken }}">
<!-- Application fields go here. -->
<div class="h-captcha" data-sitekey="{{ .HCaptchaSitekey }}"></div>
<button type="submit">Create account</button>
</form>
<script src="https://js.hcaptcha.com/1/api.js" async defer></script>
Adapt the CSRF field to the application's established protection. Confirm Content Security Policy allows the hCaptcha resources required by the deployment.
Verify the response with the Go client#
Create one client at startup and reuse it across handlers. This server example sets a five-second HTTP timeout, limits form bodies to 1 MiB, and sends the expected sitekey from server configuration:
package main
import (
"log"
"net/http"
"os"
"time"
hcaptcha "github.com/hCaptcha/hcaptcha-go/hcaptcha"
)
func main() {
sitekey := os.Getenv("HCAPTCHA_SITEKEY")
if sitekey == "" {
log.Fatal("HCAPTCHA_SITEKEY is required")
}
verifier, err := hcaptcha.NewWithHTTPClient(
os.Getenv("HCAPTCHA_SECRET"),
&http.Client{Timeout: 5 * time.Second},
)
if err != nil {
log.Fatal("hCaptcha configuration is invalid")
}
mux := http.NewServeMux()
mux.HandleFunc("POST /signup", func(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, 1<<20)
if err := r.ParseForm(); err != nil {
http.Error(w, "invalid form", http.StatusBadRequest)
return
}
// Apply your existing CSRF and other field validation here,
// or in middleware, before performing the protected action.
token := r.PostForm.Get("h-captcha-response")
if token == "" {
http.Error(w, "complete the anti-bot check", 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, "complete the anti-bot check again", http.StatusForbidden)
return
}
// Perform the protected action here.
w.WriteHeader(http.StatusNoContent)
})
server := &http.Server{
Addr: "127.0.0.1:8080",
Handler: mux,
ReadHeaderTimeout: 5 * time.Second,
}
log.Fatal(server.ListenAndServe())
}
This example supplies the POST handler; connect it to your application's template route and CSRF middleware. The client rejects an empty secret during initialization. Passing r.Context() propagates request cancellation to Siteverify.
VerifyRequest returns an error for transport failures, non-2xx responses, or invalid JSON. A valid response containing success: false can have a nil Go error. Allow the protected action only when err == nil and result["success"] == true.
Do not log the token, secret, or complete verification response. The RemoteIP request field is optional. We recommend it for improved verification accuracy and Enterprise risk scores when the application derives the visitor's IP address from a reviewed, trusted proxy configuration. Omit it when that configuration is unavailable.
Handle timeouts and retries#
hcaptcha.New(secret) uses a one-second HTTP timeout per attempt. Use NewWithHTTPClient as above to set your application's timeout. Leave Request.MaxRetries at its default of zero for one attempt, or set it deliberately for extra attempts after transport errors or HTTP 429/5xx responses.
Tokens are short-lived and single-use. A timed-out request may already have consumed the token at hCaptcha. If a retry returns already-seen-response, deny the action and obtain a fresh token. Reset or render the challenge again after a rejected submission.
Test the Go integration#
- Confirm a valid token permits the protected action exactly once.
- Reject missing, invalid, expired, reused, and wrong-sitekey tokens.
- Reject timeouts, cancellation, connection failures, non-2xx responses, malformed JSON, and
success: falsewithout performing the protected action. - Confirm secrets never appear in HTML, browser requests, logs, or error responses.
- Test allowed hostnames, request-size limits, CSRF, CSP, accessibility, and keyboard behavior.
- Verify reverse-proxy address handling before setting
RemoteIP.
The repository includes a runnable React/Vite and Go example. Its public test keys skip visual challenges; use production keys for final deployment checks. For real keys, use a configured development hostname because hCaptcha does not support localhost or 127.0.0.1 as widget hostnames.
Troubleshoot common Go problems#
The widget does not appear
Confirm the template receives the sitekey, the script loads once, the widget is inside the form, and CSP permits the required resources.
The handler always rejects submissions
Check that it reads h-captcha-response, uses the matching secret and expected sitekey, and can reach api.hcaptcha.com over HTTPS. Obtain a fresh token after a failed submission.
Verification requests time out
Check outbound connectivity, retain a finite HTTP timeout, and pass the incoming request context. Treat timeout and cancellation errors as failed verification.
Frequently asked questions#
Is there an official hCaptcha Go integration?
Yes. Use github.com/hCaptcha/hcaptcha-go/hcaptcha for server-side token verification. Your application still renders the widget and gates the protected action.
Which Go version does the client require?
The module requires Go 1.24 or later. Use a supported, patched toolchain in production.
Do I still need the older community middleware?
The official client provides the verification path in this guide. You can integrate it into a standard net/http handler or call it from your framework's handler.
Does a nil Go error mean verification passed?
Check both the error and result["success"]. A valid Siteverify response with success: false can return without a Go error.
Should I send the user's IP address?
RemoteIP is optional. We recommend it for improved verification accuracy and Enterprise risk scores. Send it only when your application derives the address from a reviewed, trusted proxy configuration.
Sources and references
- hCaptcha Go integration and source hCaptcha
- hCaptcha integrations - Go client hCaptcha
- Verify the hCaptcha response server-side hCaptcha
- hCaptcha Pro product overview hCaptcha
- hCaptcha Pro hCaptcha