Building a 30-Billion-Request-Per-Month Service with 200 Lines of Go
Hook
A service handling over 1 million requests per minute costs less than your coffee budget and fits in a single file. This is what happens when you ruthlessly prioritize simplicity over features.
Context
Every developer eventually faces the same frustrating problem: figuring out their public IP address programmatically. You'd think this would be trivial—after all, every HTTP request contains connection information. But NAT, proxies, load balancers, and cloud infrastructure create layers of indirection between your application and the internet. The IP your process sees is often a private RFC1918 address or a proxy's internal IP, not the public address the rest of the world uses to reach you.
Before reliable IP detection services, developers cobbled together fragile solutions: parsing the output of curl ifconfig.me, screenscraping websites, or maintaining brittle code that tried to connect to external services and parse varied response formats. Cloud providers offer metadata services, but these only work within their ecosystems and require platform-specific code. What the ecosystem needed was a dead-simple HTTP endpoint that returned one thing reliably: your public IP address. ipify-api emerged as that solution, deliberately eschewing features for the sake of uncompromising reliability and performance.
Technical Insight
The entire ipify-api service architecture revolves around one insight: the most reliable code is code you don't write. The application is essentially a sophisticated request handler that extracts the client IP from HTTP metadata and returns it. But the sophistication lies in handling the real-world complexity of proxy headers and connection metadata.
The core challenge is determining the "true" client IP. When requests pass through proxies, load balancers, or CDNs, the direct connection IP is often the proxy itself. The real client IP must be extracted from headers like X-Forwarded-For, X-Real-IP, or CF-Connecting-IP. ipify-api implements a cascading strategy that checks these headers in priority order:
func getClientIP(r *http.Request) string {
// Try X-Forwarded-For first (standard proxy header)
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
// XFF can contain multiple IPs: client, proxy1, proxy2
// We want the leftmost (original client)
if idx := strings.Index(xff, ","); idx != -1 {
return strings.TrimSpace(xff[:idx])
}
return strings.TrimSpace(xff)
}
// Fall back to X-Real-IP (used by nginx and others)
if xri := r.Header.Get("X-Real-IP"); xri != "" {
return xri
}
// Last resort: use the direct connection IP
ip, _, _ := net.SplitHostPort(r.RemoteAddr)
return ip
}
This header-checking logic is security-critical. If you trust the wrong header or parse X-Forwarded-For incorrectly, you might return a proxy's IP or even allow IP spoofing. The leftmost IP in X-Forwarded-For is typically the real client, but only if you trust the proxies adding subsequent entries. For Heroku's routing stack (ipify's primary deployment target), this trust model is appropriate.
The service supports multiple response formats through content negotiation. The default is plain text—literally just the IP address with no wrapper—making it trivial to use in shell scripts:
# Simple, no parsing needed
MY_IP=$(curl https://api.ipify.org)
echo "My IP is: $MY_IP"
For applications that prefer structured data, adding an Accept header or using the format parameter returns JSON:
func handleRequest(w http.ResponseWriter, r *http.Request) {
ip := getClientIP(r)
// Check if client wants JSON
if r.URL.Query().Get("format") == "json" ||
strings.Contains(r.Header.Get("Accept"), "application/json") {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"ip": ip})
return
}
// Default to plain text
w.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(w, "%s\n", ip)
}
The deployment model is equally straightforward. Go compiles to a single statically-linked binary with no external dependencies—no runtime to install, no libraries to manage, no version conflicts. This makes the Heroku deployment process trivial: the buildpack compiles the binary, and Heroku's router handles all the scaling, load balancing, and availability concerns.
The stateless architecture is what enables the extreme scale. Each request is completely independent—no database lookups, no caching layers, no shared state between requests. This means horizontal scaling is perfect: just add more dynos and the capacity increases linearly. With Go's efficient goroutine-per-request model, a single dyno can handle thousands of concurrent connections with minimal memory overhead. Response times stay consistently under 10ms because there's simply nothing slow in the request path: parse headers, format response, done.
This architectural minimalism is a feature, not a limitation. By refusing to add features like rate limiting, analytics, or geolocation, ipify-api remains focused and maintainable. Those concerns are pushed to the infrastructure layer where they belong: Heroku handles DDoS protection, monitoring tools handle observability, and users who need geolocation can layer it on top of the IP address.
Gotcha
The Heroku-centric design means you'll need to adapt the deployment for other environments. The codebase assumes Heroku's request routing model, particularly around which proxy headers to trust. If you deploy behind a different load balancer or CDN, you might need to adjust the header precedence logic. Cloudflare, for example, uses CF-Connecting-IP which should be checked first in their environment. Kubernetes ingress controllers, AWS ALBs, and GCP load balancers each have their own header conventions.
The lack of built-in rate limiting is both a strength and a weakness. For the public ipify.org service, Heroku's infrastructure handles abuse prevention. But if you self-host without similar protections, you're vulnerable to resource exhaustion from aggressive clients. A single poorly-configured script polling your endpoint every second could waste resources. You'll need to implement rate limiting at the reverse proxy level (nginx, Caddy) or use a cloud provider's built-in protections. The codebase itself has no authentication or quota mechanisms—by design, but this means operational concerns fall entirely on your deployment strategy.
Verdict
Use if: You need a simple, reliable way to detect public IP addresses at scale, want to study a minimal-but-production-grade Go web service, or need to self-host IP detection for compliance/air-gapped environments. The public ipify.org service is production-ready for most use cases, and self-hosting gives you complete control over availability and data residency. It's also an excellent learning resource—the codebase demonstrates how to handle proxy headers correctly, design stateless services, and prioritize operational simplicity. Skip if: You need IP geolocation, ASN information, or other metadata beyond the raw address—you'll want a full-featured service like ipinfo.io instead. Also skip if your deployment environment differs significantly from Heroku without the expertise to adapt the proxy header logic, or if you need built-in rate limiting and can't implement it at the infrastructure layer. For most developers, using the free public service is the right choice; self-hosting is only worthwhile for specific operational requirements.