// https://github.com/tomasen/realip package hdx import ( "errors" "net" "net/http" "strings" ) var cidrs []*net.IPNet func init() { maxCidrBlocks := []string{ "127.0.0.1/8", // localhost "10.0.0.0/8", // 24-bit block "172.16.0.0/12", // 20-bit block "192.168.0.0/16", // 16-bit block "169.254.0.0/16", // link local address "::1/128", // localhost IPv6 "fc00::/7", // unique local address IPv6 "fe80::/10", // link local address IPv6 } cidrs = make([]*net.IPNet, len(maxCidrBlocks)) for i, maxCidrBlock := range maxCidrBlocks { _, cidr, _ := net.ParseCIDR(maxCidrBlock) cidrs[i] = cidr } } func getIP(r *http.Request) string { // Fetch header value xRealIP := r.Header.Get("X-Real-Ip") xForwardedFor := r.Header.Get("X-Forwarded-For") // If both empty, return IP from remote address if xRealIP == "" && xForwardedFor == "" { var remoteIP string // If there are colon in remote address, remove the port number // otherwise, return remote address as is if strings.ContainsRune(r.RemoteAddr, ':') { remoteIP, _, _ = net.SplitHostPort(r.RemoteAddr) } else { remoteIP = r.RemoteAddr } return remoteIP } // Check list of IP in X-Forwarded-For and return the first global address for _, address := range strings.Split(xForwardedFor, ",") { address = strings.TrimSpace(address) isPrivate, err := isPrivateAddress(address) if !isPrivate && err == nil { return address } } // If nothing succeed, return X-Real-IP return xRealIP } func isPrivateAddress(address string) (bool, error) { ipAddress := net.ParseIP(address) if ipAddress == nil { return false, errors.New("address is not valid") } for i := range cidrs { if cidrs[i].Contains(ipAddress) { return true, nil } } return false, nil }