├── README.md ├── go.mod └── reqip.go /README.md: -------------------------------------------------------------------------------- 1 | # reqip 2 | A simple tool for retrieving a request's IP address on the server. Inspired from [request-ip](https://github.com/pbojinov/request-ip) 3 | 4 | # Installation 5 | Via `go get` 6 | 7 | ```bash 8 | go get github.com/mo7zayed/reqip 9 | ``` 10 | 11 | # How to use 12 | ```golang 13 | package main 14 | 15 | import ( 16 | "fmt" 17 | "net/http" 18 | "github.com/mo7zayed/reqip" 19 | ) 20 | 21 | func main() { 22 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 23 | fmt.Fprintf( 24 | w, 25 | fmt.SprintF("your ip is %s", reqip.GetClientIP(r)) // reqip.GetClientIP receives a *http.Request var type 26 | ) 27 | }) 28 | 29 | fmt.Println("server started on: http://127.0.1.1:8000") 30 | http.ListenAndServe(":8000", nil) 31 | } 32 | ``` 33 | 34 | ## How It Works 35 | 36 | It looks for specific headers in the request and falls back to some defaults if they do not exist. 37 | 38 | The user ip is determined by the following order: 39 | 40 | 1. `X-Client-IP` 41 | 2. `X-Forwarded-For` (Header may return multiple IP addresses in the format: "client IP, proxy 1 IP, proxy 2 IP", so we take the the first one.) 42 | 3. `CF-Connecting-IP` (Cloudflare) 43 | 4. `Fastly-Client-Ip` (Fastly CDN and Firebase hosting header when forwared to a cloud function) 44 | 5. `True-Client-Ip` (Akamai and Cloudflare) 45 | 6. `X-Real-IP` (Nginx proxy/FastCGI) 46 | 7. `X-Cluster-Client-IP` (Rackspace LB, Riverbed Stingray) 47 | 8. `X-Forwarded`, `Forwarded-For` and `Forwarded` (Variations of #2) 48 | 9. `http.Request.RemoteAddr` 49 | 50 | ## License 51 | The MIT License (MIT) - 2020 -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mo7zayed/reqip 2 | 3 | go 1.14 4 | 5 | require google.golang.org/grpc v1.46.0 6 | -------------------------------------------------------------------------------- /reqip.go: -------------------------------------------------------------------------------- 1 | package reqip 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "net/http" 7 | "strings" 8 | 9 | "google.golang.org/grpc/metadata" 10 | "google.golang.org/grpc/peer" 11 | ) 12 | 13 | // IsIP : Check if the given ip address is valid 14 | func isIP(ip string) bool { 15 | return net.ParseIP(ip) != nil 16 | } 17 | 18 | // GetClientIPFromXForwardedFor : Parse x-forwarded-for headers. 19 | func getClientIPFromXForwardedFor(header string) string { 20 | if header == "" { 21 | return "" 22 | } 23 | 24 | // x-forwarded-for may return multiple IP addresses in the format 25 | // @see https://en.wikipedia.org/wiki/X-Forwarded-For#Format 26 | proxies := strings.Split(header, ", ") 27 | 28 | var ips []string 29 | 30 | if len(proxies) > 0 { 31 | for _, proxy := range proxies { 32 | ip := proxy 33 | // make sure we only use this if it's ipv4 (ip:port) 34 | if strings.Contains(ip, ":") { 35 | splitted := strings.Split(ip, ":") 36 | ips = append(ips, splitted[0]) 37 | continue 38 | } 39 | ips = append(ips, ip) 40 | } 41 | } 42 | 43 | // Sometimes IP addresses in this header can be 'unknown' (http://stackoverflow.com/a/11285650). 44 | // Therefore taking the left-most IP address that is not unknown 45 | // A Squid configuration directive can also set the value to "unknown" (http://www.squid-cache.org/Doc/config/forwarded_for/) 46 | for _, ip := range ips { 47 | if isIP(ip) { 48 | return ip 49 | } 50 | } 51 | 52 | return "" 53 | } 54 | 55 | // GetClientIP : Parse all headers. 56 | func GetClientIP(r *http.Request) string { 57 | headers := r.Header 58 | 59 | if len(headers) > 0 { 60 | checklist := []string{ 61 | "x-client-ip", // Standard headers used by Amazon EC2, Heroku, and others. 62 | "x-forwarded-for", // Load-balancers (AWS ELB) or proxies. 63 | "cf-connecting-ip", // @see https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-Cloudflare-handle-HTTP-Request-headers- 64 | "fastly-client-ip", // Fastly and Firebase hosting header (When forwarded to cloud function) 65 | "true-client-ip", // Akamai and Cloudflare: True-Client-IP. 66 | "x-real-ip", // Default nginx proxy/fcgi; alternative to x-forwarded-for, used by some proxies. 67 | "x-cluster-client-ip", // (Rackspace LB and Riverbed's Stingray) http://www.rackspace.com/knowledge_center/article/controlling-access-to-linux-cloud-sites-based-on-the-client-ip-address 68 | "x-forwarded", 69 | "forwarded-for", 70 | "forwarded", 71 | } 72 | 73 | for _, h := range checklist { 74 | if h == "x-forwarded-for" { 75 | if ip := getClientIPFromXForwardedFor(r.Header.Get(h)); isIP(ip) { 76 | return ip 77 | } 78 | continue 79 | } 80 | 81 | if ip := r.Header.Get(h); isIP(ip) { 82 | return ip 83 | } 84 | } 85 | } 86 | 87 | if ip := r.RemoteAddr; isIP(ip) { 88 | return ip 89 | } 90 | 91 | return "" 92 | } 93 | 94 | // GetClientIPgRPC : Parse all headers. 95 | func GetClientIPgRPC(ctx context.Context) string { 96 | md, ok := metadata.FromIncomingContext(ctx) 97 | if !ok { 98 | return "" 99 | } 100 | 101 | if md.Len() > 0 { 102 | checklist := []string{ 103 | "x-client-ip", // Standard headers used by Amazon EC2, Heroku, and others. 104 | "x-forwarded-for", // Load-balancers (AWS ELB) or proxies. 105 | "cf-connecting-ip", // @see https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-Cloudflare-handle-HTTP-Request-headers- 106 | "fastly-client-ip", // Fastly and Firebase hosting header (When forwarded to cloud function) 107 | "true-client-ip", // Akamai and Cloudflare: True-Client-IP. 108 | "x-real-ip", // Default nginx proxy/fcgi; alternative to x-forwarded-for, used by some proxies. 109 | "x-cluster-client-ip", // (Rackspace LB and Riverbed's Stingray) http://www.rackspace.com/knowledge_center/article/controlling-access-to-linux-cloud-sites-based-on-the-client-ip-address 110 | "x-forwarded", 111 | "forwarded-for", 112 | "forwarded", 113 | } 114 | 115 | for _, h := range checklist { 116 | if h == "x-forwarded-for" { 117 | header := md.Get(h) 118 | if ip := getClientIPFromXForwardedFor(header[0]); isIP(ip) { 119 | return ip 120 | } 121 | continue 122 | } 123 | 124 | if ip := md.Get(h); isIP(ip[0]) { 125 | return ip[0] 126 | } 127 | } 128 | } 129 | 130 | p, _ := peer.FromContext(ctx) 131 | hostPort := strings.Split(p.Addr.String(), ":") 132 | return hostPort[0] 133 | } 134 | --------------------------------------------------------------------------------