Skip to content

Commit

Permalink
Update README with enhanced IP detection features
Browse files Browse the repository at this point in the history
Introduce detailed descriptions of the module's IP detection capabilities, including IPv4 and IPv6 support, header customization, and integration options. Add sections for best practices, troubleshooting, and supported headers.
  • Loading branch information
dmitrymomot committed Oct 29, 2024
1 parent c9371d0 commit 412bc9b
Showing 1 changed file with 79 additions and 30 deletions.
109 changes: 79 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@
[![GolangCI Lint](https://github.com/dmitrymomot/clientip/actions/workflows/golangci-lint.yml/badge.svg)](https://github.com/dmitrymomot/clientip/actions/workflows/golangci-lint.yml)
[![Go Report Card](https://goreportcard.com/badge/github.com/dmitrymomot/clientip)](https://goreportcard.com/report/github.com/dmitrymomot/clientip)

This Go module provides functionality to accurately determine a client's IP address from HTTP requests, considering various headers that might contain the real IP address, especially when your application is behind a proxy or load balancer.
A lightweight, production-ready Go module for accurately determining client IP addresses in HTTP applications. Particularly useful for applications behind proxies, load balancers, or CDNs like Cloudflare, DigitalOcean App Platform, and Fastly.

## Features

- **LookupFromRequest**: Retrieve the client IP address directly from an HTTP request with consideration for custom headers.
- **Middleware**: An easy-to-integrate middleware function for HTTP servers that automatically sets the `RemoteAddr` field of the request to the client's real IP address.
- **Accurate IP Detection**: Intelligently extracts client IPs from various standard and vendor-specific headers
- **IPv4 & IPv6 Support**: Full support for both IPv4 and IPv6 addresses with proper canonicalization
- **Flexible Header Configuration**: Use default headers or specify custom ones for your specific setup
- **Multiple Integration Options**:
- Direct IP lookup function for manual integration
- Drop-in middleware for automatic IP detection
- Context-based IP storage for thread-safe access
- **Production-Ready**: Used in production environments with comprehensive test coverage

## Installation

Expand All @@ -26,64 +32,107 @@ go get github.com/dmitrymomot/clientip

## Usage

### LookupFromRequest Function

`LookupFromRequest` retrieves the client IP address from the provided HTTP request. It checks a list of headers for the IP address and uses a default set if none are specified.
### Direct IP Lookup

```go
import "github.com/dmitrymomot/clientip"

func handler(w http.ResponseWriter, r *http.Request) {
clientIP := clientip.LookupFromRequest(r)
fmt.Fprintf(w, "Client IP: %s", clientIP)
// Use default headers
ip := clientip.LookupFromRequest(r)

// Or specify custom headers
ip = clientip.LookupFromRequest(r, "X-Custom-IP", "X-Real-IP")

fmt.Fprintf(w, "Your IP: %s", ip)
}
```

With custom headers:
### Middleware Integration

```go
import "github.com/dmitrymomot/clientip"
func main() {
mux := http.NewServeMux()

func handler(w http.ResponseWriter, r *http.Request) {
clientIP := clientip.LookupFromRequest(r, "X-Custom-Real-IP")
fmt.Fprintf(w, "Client IP: %s", clientIP)
// Basic middleware usage with default headers
handler := clientip.Middleware()(mux)

// Or with custom headers
handler = clientip.Middleware("X-Custom-IP", "CF-Connecting-IP")(mux)

http.ListenAndServe(":8080", handler)
}
```

### Middleware

The `Middleware` function returns a middleware handler that modifies the request's `RemoteAddr` based on the client IP address found in the specified headers.
### Context-Based IP Access

```go
import (
"net/http"
"github.com/dmitrymomot/clientip"
)

func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", handler)

// Init the middleware with custom header. If not specified, the default headers are used.
mdw := clientip.Middleware("X-Custom-Real-IP")
// Chain both middlewares
handler := clientip.Middleware()(mux)
handler = clientip.IpToContext(handler) // Store IP in context, it must be called after Middleware

http.ListenAndServe(":8080", mdw(mux))
http.ListenAndServe(":8080", handler)
}

func handler(w http.ResponseWriter, r *http.Request) {
// The RemoteAddr now contains the real client IP
fmt.Fprintf(w, "Client IP: %s", r.RemoteAddr)
// Get IP from context anywhere in your handler chain
if ip := clientip.GetIPAddress(r.Context()); ip != "" {
fmt.Fprintf(w, "Your IP from context: %s", ip)
}
}
```

### Supported Headers

The module checks the following headers by default (in order):

- `DO_Connecting-IP`, `DO-Connecting-IP` (DigitalOcean)
- `True-Client-IP`
- `X-Real-IP`
- `CF-Connecting-IP` (Cloudflare)
- `Fastly-Client-IP`
- `X-Cluster-Client-IP`
- `X-Client-IP`
- `X-Forwarded-For` (first IP in the chain)

You can override these by providing your own headers to the functions.

## Customization

Both `LookupFromRequest` and `Middleware` functions accept an optional list of headers to consider when looking for the client IP address. By default, a predefined set of common headers used for forwarding client IPs in proxy setups are checked.

## Best Practices

- **Security**: Always validate and sanitize IP addresses before using them in security-critical contexts
- **Header Order**: Configure headers in order of trust (most trusted first)
- **Performance**: Use the middleware approach for consistent IP handling across your application
- **Context Usage**: Prefer context-based IP access when working with complex handler chains

## Troubleshooting

### Common Issues

1. **Empty IP Address**

- Check if your proxy/load balancer is properly configured to forward IP headers
- Verify the header names match your infrastructure setup

2. **Incorrect IP Address**

- Ensure headers are being set in the correct order of precedence
- Check if intermediate proxies are modifying the headers

3. **IPv6 Handling**
- The module automatically canonicalizes IPv6 addresses to their /64 prefix
- If you need the full address, use the raw header value instead

For more help, please open an issue on GitHub.

## Contributing

If you wish to contribute to this project, please fork the repository and submit a pull request.

## License

This project is licensed under the [Apache 2.0](LICENSE) - see the `LICENSE` file for details.
This project is licensed under the [Apache 2.0](LICENSE) - see the `LICENSE` file for details.

0 comments on commit 412bc9b

Please sign in to comment.