diff --git a/docs/api/ctx.md b/docs/api/ctx.md index 010d7425f48..a4941254299 100644 --- a/docs/api/ctx.md +++ b/docs/api/ctx.md @@ -1145,14 +1145,45 @@ app.Get("/", func(c fiber.Ctx) error { }) ``` -When registering the proxy request header in the Fiber app, the IP address of the header is returned [(Fiber configuration)](fiber.md#proxyheader) +:::info +By default, `c.IP()` returns the remote IP address from the TCP connection. When your Fiber app is behind a reverse proxy (like Nginx, Traefik, or a load balancer), you need to configure **both** [`TrustProxy`](fiber.md#trustproxy) and [`ProxyHeader`](fiber.md#proxyheader) to read the client IP from proxy headers like `X-Forwarded-For`. -```go +**Important:** You must enable `TrustProxy` and configure trusted proxy IPs to prevent header spoofing. Simply setting `ProxyHeader` alone will not work. + +**Note:** When using a proxy header such as `X-Forwarded-For`, `c.IP()` returns the raw header value unless [`EnableIPValidation`](fiber.md#enableipvalidation) is enabled. For `X-Forwarded-For`, this raw value may be a comma-separated list of IPs; enable `EnableIPValidation` if you need `c.IP()` to return a single, validated client IP. +::: + +#### Configuration for apps behind a reverse proxy + +```go title="Example - Basic Configuration" +app := fiber.New(fiber.Config{ + // Enable proxy support + TrustProxy: true, + // Specify which header contains the real client IP + ProxyHeader: fiber.HeaderXForwardedFor, + // Configure which proxy IPs to trust + TrustProxyConfig: fiber.TrustProxyConfig{ + // Trust private IP ranges (for internal load balancers) + Private: true, + // Or specify exact proxy IPs/ranges + // Proxies: []string{"10.10.0.58", "192.168.0.0/24"}, + }, +}) +``` + +```go title="Example - Specific Proxy IPs" app := fiber.New(fiber.Config{ + TrustProxy: true, ProxyHeader: fiber.HeaderXForwardedFor, + TrustProxyConfig: fiber.TrustProxyConfig{ + // Trust only specific proxy IP addresses + Proxies: []string{"10.10.0.58", "192.168.1.0/24"}, + }, }) ``` +See [`TrustProxy`](fiber.md#trustproxy) and [`TrustProxyConfig`](fiber.md#trustproxyconfig) for more details on security considerations and configuration options. + ### IPs Returns an array of IP addresses specified in the [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For) request header. diff --git a/docs/api/fiber.md b/docs/api/fiber.md index 5863ba9860b..0b61b7115c1 100644 --- a/docs/api/fiber.md +++ b/docs/api/fiber.md @@ -71,7 +71,7 @@ app := fiber.New(fiber.Config{ | MsgPackEncoder | `utils.MsgPackMarshal` | Allowing for flexibility in using another msgpack library for encoding. | `binder.UnimplementedMsgpackMarshal` | | PassLocalsToContext | `bool` | Controls whether `StoreInContext` also propagates values into the request `context.Context` for Fiber-backed contexts. `StoreInContext` always writes to `c.Locals()`. `ValueFromContext` for Fiber-backed contexts always reads from `c.Locals()`. | `false` | | PassLocalsToViews | `bool` | PassLocalsToViews Enables passing of the locals set on a fiber.Ctx to the template engine. See our **Template Middleware** for supported engines. | `false` | -| ProxyHeader | `string` | This will enable `c.IP()` to return the value of the given header key. By default `c.IP()`will return the Remote IP from the TCP connection, this property can be useful if you are behind a load balancer e.g. _X-Forwarded-\*_. | `""` | +| ProxyHeader | `string` | Specifies the header name to read the client's real IP address from when behind a reverse proxy. Common values: `fiber.HeaderXForwardedFor`, `"X-Real-IP"`, `"CF-Connecting-IP"` (Cloudflare).

**Important:** This setting **requires** `TrustProxy` to be enabled; `TrustProxyConfig` controls which proxy IPs are trusted for reading this header. Without `TrustProxy`, this setting has no effect and `c.IP()` will always return the remote IP from the TCP connection.

**Behavior note:** `X-Forwarded-For` often contains a comma-separated chain of IP addresses. With the default `EnableIPValidation = false`, `c.IP()` will return the raw header value (the whole chain) rather than a single parsed client IP. With `EnableIPValidation = true`, `c.IP()` parses the header and returns the **first syntactically valid IP address** it finds; it does **not** walk the chain to find the first non-proxy hop. For a reliable client IP, configure your reverse proxy to overwrite or sanitize this header and/or to provide a single-IP header such as `"X-Real-IP"` or a provider-specific header like `"CF-Connecting-IP"`.

**Security Warning:** Headers can be easily spoofed. Always configure `TrustProxyConfig` to validate the proxy IP address, otherwise malicious clients can forge headers to bypass IP-based access controls. | `""` | | ReadBufferSize | `int` | per-connection buffer size for requests' reading. This also limits the maximum header size. Increase this buffer if your clients send multi-KB RequestURIs and/or multi-KB headers \(for example, BIG cookies\). | `4096` | | ReadTimeout | `time.Duration` | The amount of time allowed to read the full request, including the body. The default timeout is unlimited. | `0` | | ReduceMemoryUsage | `bool` | Aggressively reduces memory usage at the cost of higher CPU usage if set to true. | `false` | @@ -80,8 +80,8 @@ app := fiber.New(fiber.Config{ | StreamRequestBody | `bool` | StreamRequestBody enables request body streaming, and calls the handler sooner when given body is larger than the current limit. | `false` | | StrictRouting | `bool` | When enabled, the router treats `/foo` and `/foo/` as different. Otherwise, the router treats `/foo` and `/foo/` as the same. | `false` | | StructValidator | `StructValidator` | If you want to validate header/form/query... automatically when to bind, you can define struct validator. Fiber doesn't have default validator, so it'll skip validator step if you don't use any validator. | `nil` | -| TrustProxy | `bool` | When true, Fiber validates the proxy IP against `TrustProxyConfig.Proxies`.

By default, `c.Protocol()`, `c.IP()`, and `c.Hostname()` read values from standard X-Forwarded headers. If the remote IP matches a trusted proxy, these methods behave as if `TrustProxy` were disabled. Otherwise, `c.Protocol()` reflects the connection scheme, `c.IP()` uses `RemoteIP()` from Fasthttp, and `c.Hostname()` uses `fasthttp.Request.URI().Host()` | `false` | -| TrustProxyConfig | `TrustProxyConfig` | Configure trusted proxy IP's. Look at `TrustProxy` doc.

`TrustProxyConfig.Proxies` can take IP or IP range addresses. | `nil` | +| TrustProxy | `bool` | Enables trust of reverse proxy headers. When enabled, Fiber will check if the request is coming from a trusted proxy (configured in `TrustProxyConfig`) before reading values from proxy headers.

**Required for**: Using `ProxyHeader` to read client IP from headers like `X-Forwarded-For`.

**Behavior when enabled:** If the remote IP is trusted (matches `TrustProxyConfig`), then `c.IP()` reads from `ProxyHeader` (when configured; otherwise it uses `RemoteIP()`), `c.Scheme()` first checks standard proxy scheme headers (`X-Forwarded-Proto`, `X-Forwarded-Protocol`, `X-Forwarded-Ssl`, `X-Url-Scheme`) and falls back to the actual connection scheme if none are set, and `c.Hostname()` prefers `X-Forwarded-Host` but falls back to the request Host header when the proxy header is not present. If the remote IP is NOT trusted, these methods ignore proxy headers and use the actual connection values instead.

**Security:** This prevents header spoofing by validating the proxy's IP address. Always configure `TrustProxyConfig` when enabling this option and set `ProxyHeader` if you want `c.IP()` to use a specific header. | `false` | +| TrustProxyConfig | `TrustProxyConfig` | Configures which proxy IP addresses or ranges to trust. Only effective when `TrustProxy` is enabled.

**Fields:**
• `Proxies` - List of trusted proxy IPs or CIDR ranges (e.g., `[]string{"10.10.0.58", "192.168.0.0/24"}`)
• `Loopback` - Trust loopback addresses (127.0.0.0/8, ::1/128)
• `Private` - Trust all private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00::/7)
• `LinkLocal` - Trust link-local addresses (169.254.0.0/16, fe80::/10)
• `UnixSocket` - Trust Unix domain socket connections

**Example:** For an app behind Nginx at 10.10.0.58, use `TrustProxyConfig{Proxies: []string{"10.10.0.58"}}` or `TrustProxyConfig{Private: true}` if using private network IPs. | `{}` | | UnescapePath | `bool` | Converts all encoded characters in the route back before setting the path for the context, so that the routing can also work with URL encoded special characters | `false` | | Views | `Views` | Views is the interface that wraps the Render function. See our **Template Middleware** for supported engines. | `nil` | | ViewsLayout | `string` | Views Layout is the global layout for all template render until override on Render function. See our **Template Middleware** for supported engines. | `""` | diff --git a/docs/guide/reverse-proxy.md b/docs/guide/reverse-proxy.md index 9c4c5e6fafc..1d880279b2c 100644 --- a/docs/guide/reverse-proxy.md +++ b/docs/guide/reverse-proxy.md @@ -3,7 +3,7 @@ id: reverse-proxy title: 🔄 Reverse Proxy Configuration description: >- Learn how to set up reverse proxies like Nginx or Traefik to enable modern - HTTP capabilities in your Fiber application, including HTTP/2 and + HTTP capabilities in your Fiber application, including HTTP/2 and HTTP/3 (QUIC) support. This guide also covers basic reverse proxy configuration and links to external documentation. sidebar_position: 4 @@ -11,7 +11,7 @@ sidebar_position: 4 ## Reverse Proxies -Running Fiber behind a reverse proxy is a common production setup. +Running Fiber behind a reverse proxy is a common production setup. Reverse proxies can handle: - **HTTPS/TLS termination** (offloading SSL certificates) @@ -29,12 +29,76 @@ Some Fiber features (like [`SendEarlyHints`](../api/ctx.md#sendearlyhints)) requ - [HA PROXY](https://www.haproxy.com/) - [Caddy](https://caddyserver.com/) -### Enabling HTTP/2 +## Getting the Real Client IP Address -Popular choices include Nginx and Traefik. +When your Fiber application is behind a reverse proxy, the TCP connection comes from the proxy server, not the actual client. To get the real client IP address, you need to configure Fiber to read it from proxy headers like `X-Forwarded-For`. -
-Nginx Example +:::warning Security Warning +Proxy headers can be easily spoofed by malicious clients. **Always** configure `TrustProxyConfig` to validate the proxy IP address, otherwise attackers can forge headers to bypass IP-based access controls, rate limiting, or geolocation features. + +In addition, your reverse proxy should be configured to **set or overwrite** the forwarding header you choose (for example, `X-Forwarded-For`) based on the real client connection, or to use its real IP / PROXY protocol features. Do not simply pass through client-supplied forwarding headers, or `c.IP()` may still be controlled by an attacker even when `TrustProxyConfig` is correct. +::: + +### Configuration + +To enable reading the client IP from proxy headers, you must configure **three settings**: + +1. **`TrustProxy`** - Enable proxy header trust (must be `true`) +2. **`ProxyHeader`** - Specify which header contains the client IP +3. **`TrustProxyConfig`** - Define which proxy IPs to trust + +```go title="Example - App Behind Nginx" +app := fiber.New(fiber.Config{ + // Enable proxy support + TrustProxy: true, + + // Read client IP from X-Forwarded-For header + ProxyHeader: fiber.HeaderXForwardedFor, + + // Trust requests from your Nginx proxy + TrustProxyConfig: fiber.TrustProxyConfig{ + // Option 1: Trust specific proxy IPs + Proxies: []string{"10.10.0.58", "192.168.1.0/24"}, + + // Option 2: Or trust all private IPs (useful for internal load balancers) + // Private: true, + }, +}) +``` + +### Common Proxy Headers + +Different proxies use different headers: + +| Proxy/Service | Recommended Header | Config Value | +|---------------|-------------------|--------------| +| Nginx, HAProxy, Apache | X-Forwarded-For | `fiber.HeaderXForwardedFor` | +| Cloudflare | CF-Connecting-IP | `"CF-Connecting-IP"` | +| Fastly | Fastly-Client-IP | `"Fastly-Client-IP"` | +| Generic | X-Real-IP | `"X-Real-IP"` | + +### TrustProxyConfig Options + +The `TrustProxyConfig` struct provides multiple ways to specify trusted proxies: + +```go +TrustProxyConfig: fiber.TrustProxyConfig{ + // Specific IPs or CIDR ranges + Proxies: []string{ + "10.10.0.58", // Single IP + "192.168.0.0/24", // CIDR range + "2001:db8::/32", // IPv6 range + }, + + // Or use convenience flags: + Loopback: true, // Trust 127.0.0.0/8, ::1/128 + LinkLocal: true, // Trust 169.254.0.0/16, fe80::/10 + Private: true, // Trust 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00::/7 + UnixSocket: true, // Trust Unix domain socket connections +}, +``` + +### Complete Example with Nginx ```nginx title="nginx.conf" server { @@ -56,7 +120,59 @@ server { } ``` -This configuration enables HTTP/2 with TLS and proxies requests to your Fiber app on port 3000. +```go title="main.go" +package main + +import ( + "log" + + "github.com/gofiber/fiber/v3" +) + +func main() { + app := fiber.New(fiber.Config{ + TrustProxy: true, + ProxyHeader: fiber.HeaderXForwardedFor, + EnableIPValidation: true, + TrustProxyConfig: fiber.TrustProxyConfig{ + // Trust localhost since Nginx is on the same machine + Loopback: true, + }, + }) + + app.Get("/", func(c fiber.Ctx) error { + // This will now return the real client IP from X-Forwarded-For + // instead of 127.0.0.1 + return c.SendString("Your IP: " + c.IP()) + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +### Testing Your Configuration + +You can verify your configuration is working: + +```go +app.Get("/debug", func(c fiber.Ctx) error { + return c.JSON(fiber.Map{ + "c.IP()": c.IP(), // Should show real client IP + "X-Forwarded-For": c.Get("X-Forwarded-For"), // Raw header value + "IsProxyTrusted": c.IsProxyTrusted(), // Should be true + "RemoteIP": c.RequestCtx().RemoteIP().String(), // Proxy IP + }) +}) +``` + +## Enabling HTTP/2 + +Popular choices include Nginx and Traefik. + +
+Nginx Example + +See the [Complete Example with Nginx](#complete-example-with-nginx) above for a full configuration with HTTP/2 enabled.
Traefik Example @@ -85,7 +201,7 @@ http: With this configuration, Traefik terminates TLS and serves your app over HTTP/2.
-### HTTP/3 (QUIC) Support +## HTTP/3 (QUIC) Support Early Hints (103 responses) are defined for HTTP and can be delivered over HTTP/1.1 and HTTP/2/3. In practice, browsers process 103 most reliably over HTTP/2/3. Many reverse proxies also support HTTP/3 (QUIC): diff --git a/docs/whats_new.md b/docs/whats_new.md index 56ee957b69d..275b4172833 100644 --- a/docs/whats_new.md +++ b/docs/whats_new.md @@ -75,7 +75,7 @@ We have made several changes to the Fiber app, including: - `EnablePrefork` (previously `Prefork`) - `EnablePrintRoutes` - `ListenerNetwork` (previously `Network`) -- **Trusted Proxy Configuration**: The `EnabledTrustedProxyCheck` has been moved to `app.Config.TrustProxy`, and `TrustedProxies` has been moved to `TrustProxyConfig.Proxies`. +- **Trusted Proxy Configuration**: The `EnabledTrustedProxyCheck` has been moved to `app.Config.TrustProxy`, and `TrustedProxies` has been moved to `TrustProxyConfig.Proxies`. Additionally, `ProxyHeader` must be set to read client IPs from proxy headers (e.g., `X-Forwarded-For`). - **XMLDecoder Config Property**: The `XMLDecoder` property has been added to allow usage of 3rd-party XML libraries in XML binder. ### New Methods @@ -1764,6 +1764,8 @@ You have to put `*` to the end of the route if you don't define static route wit We've renamed `EnableTrustedProxyCheck` to `TrustProxy` and moved `TrustedProxies` to `TrustProxyConfig`. +**Important:** To use proxy headers like `X-Forwarded-For` with `c.IP()`, you must configure **all** of `TrustProxy`, `ProxyHeader`, and a trusted proxy via `TrustProxyConfig`. If the proxy is not trusted (for example, if you set only `ProxyHeader` or only `TrustProxy` without configuring `TrustProxyConfig`), proxy headers are ignored and `c.IP()` will return the remote TCP IP instead. + ```go // Before app := fiber.New(fiber.Config{ @@ -1779,6 +1781,8 @@ app := fiber.New(fiber.Config{ app := fiber.New(fiber.Config{ // TrustProxy enables the trusted proxy check TrustProxy: true, + // ProxyHeader specifies which header to read the real client IP from + ProxyHeader: fiber.HeaderXForwardedFor, // TrustProxyConfig allows for configuring trusted proxies. TrustProxyConfig: fiber.TrustProxyConfig{ // Proxies is a list of trusted proxy IP ranges/addresses. @@ -1787,10 +1791,12 @@ app := fiber.New(fiber.Config{ Loopback: true, // Trust Unix domain socket connections UnixSocket: true, - } + }, }) ``` +For detailed proxy configuration guidance, see the [reverse proxy guide](./guide/reverse-proxy.md). + ### 🎣 Hooks `OnShutdown` has been replaced by two hooks: `OnPreShutdown` and `OnPostShutdown`.