Go's standard library HTTP server and popular frameworks like Gin and Echo are an excellent match for Tor hidden service hosting. Go's low memory footprint and high concurrency model mean a single binary can handle significant load on minimal VPS resources. Unlike interpreted languages, a compiled Go binary has no runtime dependency, simplifying deployment. This guide covers building a Go API server for .onion deployment, including socket binding configuration, operational security practices, and deployment strategies that maintain anonymity.
Need this done for your project?
We implement, you ship. Async, documented, done in days.
Configuring Go HTTP Server for Localhost-Only Binding
Go's net/http server binds to all interfaces by default when using http.ListenAndServe(':8080'). For a Tor hidden service, change the bind address to localhost only: http.ListenAndServe('127.0.0.1:8080', handler). This single change ensures the Go application is not reachable from clearnet IP addresses even if the Tor configuration is not active. For Gin framework: r := gin.Default(); r.Run('127.0.0.1:8080'). For Echo: e.Start('127.0.0.1:8080'). For the standard library with graceful shutdown: create a custom http.Server with Addr: '127.0.0.1:8080' and use ListenAndServe on the custom server struct, then call Shutdown in a signal handler for SIGTERM/SIGINT. The Tor hidden service configuration maps the .onion port to this localhost address. Go's HTTP server handles concurrent connections efficiently using goroutines - each request runs in its own goroutine, enabling high concurrency without explicit threading. This model is particularly well-suited to hidden service hosting where connection latency is high (Tor overhead) but many clients connect concurrently.
Removing Metadata from Go API Responses
Go's net/http sets a default Server response header as 'Go-http-server' which reveals the backend language. Remove this to prevent fingerprinting. Middleware to strip the header: in a handler wrapper, delete the Server header with w.Header().Del('Server') and set a generic alternative with w.Header().Set('Server', 'nginx'). For Gin: use a custom middleware function that modifies the response header before passing to the next handler. Additional metadata removal: stack traces in error responses can reveal file paths and function names that identify your application structure. Use a production error handler that returns generic error messages and logs the full details server-side only. For JSON APIs, ensure error response structures do not include internal paths, database query errors (which can reveal schema details), or version information. Disable debug mode in frameworks: Gin's default mode is debug, which logs every route. Switch to production mode with gin.SetMode(gin.ReleaseMode) before building production binaries.
Go Binary Deployment and Secret Management
Deploying a Go binary is simpler than interpreted language deployments - compile once, copy binary, run. Build for the target platform: GOOS=linux GOARCH=amd64 go build -o myapi ./cmd/api. Transfer the binary to the server via SCP over Tor (scp via torsocks or via the server's SSH .onion address). Create a systemd service unit file that runs the binary, specifying User (non-root), WorkingDirectory, and EnvironmentFile for configuration. The EnvironmentFile points to a file containing environment variables (DATABASE_URL, API_KEYS, etc.) with chmod 600 permissions. Go applications commonly use environment variables for configuration (12-factor app pattern), making this natural. For secret management without environment variable exposure, consider using Go's flag package for command-line arguments (passed in the systemd unit ExecStart line) or reading from a chmod 400 file at startup. Avoid embedding secrets in the binary itself (even constants visible in strings). Use go build -ldflags '-s -w' to strip debug symbols, reducing binary size and removing debug metadata.
Rate Limiting and Abuse Prevention for Go Onion APIs
Go API servers on Tor hidden services face a challenge: all Tor users appear to come from Tor exit relays or internal Tor paths (for hidden service clients, there is no visible IP at all - connections arrive from the Tor process locally). IP-based rate limiting identifies all hidden service clients as the same source. Alternative rate limiting strategies: API key-based limiting (issue keys during registration, rate limit per key), user account-based limiting (for authenticated endpoints), and work-factor limiting (require solving a lightweight proof-of-work challenge for anonymous access, similar to Tor's hidden service PoW defense mechanism). Go libraries for rate limiting: golang.org/x/time/rate provides a token bucket implementation. Redis-backed rate limiting (via go-redis) is more appropriate for cluster mode. For public .onion APIs without authentication, consider CAPTCHA challenges for unusual request patterns rather than hard rate limits that would block legitimate Tor users. The Tor Browser Bundle includes heuristics for detecting unusual JavaScript - test any client-side CAPTCHA in Tor Browser compatibility mode.
Monitoring and Health Checks Without Clearnet Exposure
Monitoring a Go API .onion service without clearnet exposure requires all observability infrastructure to be within the Tor overlay or local to the server. Prometheus metrics: Go's net/http/pprof and the prometheus/client_golang library expose metrics on a separate localhost port. Create a dedicated Tor hidden service for the metrics endpoint - scrape only from a Prometheus instance also within the Tor overlay or from localhost. Structured logging with zerolog or zap to local log files avoids clearnet log aggregation services. Log rotation with logrotate ensures logs do not grow unbounded. Health check endpoints: expose a /healthz endpoint returning a simple JSON status. Use a local cron job with torsocks curl to check the .onion health endpoint and write results to a status file, or send alerts via a Tor-routed notification channel (SMTP via .onion, or a Tor-accessible messaging API). For application performance monitoring, pprof profiling endpoints provide goroutine stacks, heap profiles, and CPU profiles - expose these only on a localhost-only port (never via the public hidden service) to prevent information disclosure.