﻿# ServiceControl Forward Headers for Reverse Proxy Configuration


When ServiceControl instances are deployed behind a reverse proxy that terminates SSL/TLS (like NGINX, Traefik, or a cloud load balancer), you need to configure forwarded headers so ServiceControl correctly understands the original client request.

## Configuration

ServiceControl instances can be configured via environment variables or App.config. Each instance type uses a different prefix. See the [Hosting Guide](../hosting-guide.md) for example usage of these configuration settings in conjunction with [Authentication](authentication.md) and [TLS](tls.md) configuration settings in a scenario based format.

- [Primary Instance](/servicecontrol/servicecontrol-instances/configuration.md#forwarded-headers)
- [Audit Instance](/servicecontrol/audit-instances/configuration.md#forwarded-headers)
- [Monitoring Instance](/servicecontrol/monitoring-instances/configuration.md#forwarded-headers)

## What Headers are Processed

When enabled, ServiceControl instances process:

- `X-Forwarded-For` - Original client IP address
- `X-Forwarded-Proto` - Original protocol (http/https)
- `X-Forwarded-Host` - Original host header

When the proxy is trusted:

- `Request.Scheme` will be set from `X-Forwarded-Proto` (e.g. `https`)
- `Request.Host` will be set from `X-Forwarded-Host` (e.g. `servicecontrol.example.com`)
- Client IP will be available from `X-Forwarded-For`

When the proxy is **not** trusted (incorrect `KnownProxies`):

- `X-Forwarded-*` headers are **ignored** (not applied to the request)
- `Request.Scheme` remains `http`
- `Request.Host` remains the internal hostname
- The request is still processed (not blocked)

## HTTP to HTTPS Redirect

When using a reverse proxy that terminates SSL, ServiceControl instances can be configured to redirect HTTP requests to HTTPS. This works in combination with forwarded headers:

1. The reverse proxy forwards both HTTP and HTTPS requests to ServiceControl
2. The proxy sets `X-Forwarded-Proto` to indicate the original protocol
3. ServiceControl reads this header (via forwarded headers processing)
4. If the original request was HTTP and redirect is enabled, ServiceControl returns a redirect to HTTPS

```mermaid
sequenceDiagram
    participant Client
    participant Proxy as Reverse Proxy
    participant SC as ServiceControl

    Client->>Proxy: HTTP Request
    Note over Proxy: Terminates request<br/>Sets X-Forwarded-Proto: http
    Proxy->>SC: Request + X-Forwarded-Proto: http
    Note over SC: Reads X-Forwarded-Proto<br/>Detects original was HTTP
    SC-->>Proxy: 301 Redirect to HTTPS
    Proxy-->>Client: 301 Redirect to HTTPS
    Client->>Proxy: HTTPS Request
    Note over Proxy: Terminates SSL<br/>Sets X-Forwarded-Proto: https
    Proxy->>SC: Request + X-Forwarded-Proto: https
    SC->>Client: Response (via Proxy)
```

To enable HTTP to HTTPS redirect, see [TLS Configuration](tls.md).

## Proxy Chain Behavior (ForwardLimit)

When processing `X-Forwarded-For` headers with multiple IPs (proxy chains), the behavior depends on trust configuration:

| Configuration             | ForwardLimit      | Behavior                                      |
|---------------------------|-------------------|-----------------------------------------------|
| `TrustAllProxies = true`  | `null` (no limit) | Processes all IPs, returns original client IP |
| `TrustAllProxies = false` | `1` (default)     | Processes only the last proxy IP              |

For example, with `X-Forwarded-For: 203.0.113.50, 10.0.0.1, 192.168.1.1`:

- **TrustAllProxies = true**: Returns `203.0.113.50` (original client)
- **TrustAllProxies = false**: Returns `192.168.1.1` (last proxy)

## Configuration examples

The following examples show common forwarded headers configurations for different deployment scenarios using the primary ServiceControl instance.

### Single reverse proxy (known IP)

When running behind a single reverse proxy with a known IP address:

```xml
<add key="ServiceControl/ForwardedHeaders.Enabled" value="true" />
<add key="ServiceControl/ForwardedHeaders.TrustAllProxies" value="false" />
<add key="ServiceControl/ForwardedHeaders.KnownProxies" value="10.0.0.5" />
```

### Multiple reverse proxies

When running behind multiple proxies (e.g. load balancer and application gateway):

```xml
<add key="ServiceControl/ForwardedHeaders.Enabled" value="true" />
<add key="ServiceControl/ForwardedHeaders.TrustAllProxies" value="false" />
<add key="ServiceControl/ForwardedHeaders.KnownProxies" value="10.0.0.5,10.0.0.6" />
```

### Container/Kubernetes environment

When running in a container environment where proxy IPs are dynamic, trust a network range:

```xml
<add key="ServiceControl/ForwardedHeaders.Enabled" value="true" />
<add key="ServiceControl/ForwardedHeaders.TrustAllProxies" value="false" />
<add key="ServiceControl/ForwardedHeaders.KnownNetworks" value="10.0.0.0/8" />
```

### Development/trusted environment

For development or fully trusted environments (not recommended for production):

```xml
<add key="ServiceControl/ForwardedHeaders.Enabled" value="true" />
<add key="ServiceControl/ForwardedHeaders.TrustAllProxies" value="true" />
```

## Troubleshooting

### HTTPS redirect loops

**Symptom**: Browser shows "too many redirects" or redirect loop errors when accessing the application through a reverse proxy with SSL termination.

**Cause**: The `X-Forwarded-Proto` header is not being processed, so the application thinks the request is HTTP and keeps redirecting to HTTPS.

**Solutions**:

- Verify forwarded headers are enabled
- Check that the proxy IP is in `KnownProxies` or `KnownNetworks`, or enable `TrustAllProxies`
- Confirm the reverse proxy is sending the `X-Forwarded-Proto` header

### Wrong host in generated URLs

**Symptom**: Links or redirects use the internal hostname (e.g. `localhost` or container name) instead of the public hostname.

**Cause**: The `X-Forwarded-Host` header is not being trusted or processed.

**Solutions**:

- Verify the proxy IP is trusted (check `KnownProxies`/`KnownNetworks` configuration)
- Confirm the reverse proxy is sending the `X-Forwarded-Host` header
- In Kubernetes, ensure the correct pod/node network is in `KnownNetworks`

### Client IP shows proxy IP instead of real client

**Symptom**: Logs or audit trails show the proxy's IP address instead of the actual client IP.

**Cause**: The `X-Forwarded-For` header is not being processed, or the wrong IP is being extracted from a proxy chain.

**Solutions**:

- Verify forwarded headers are enabled and the proxy is trusted
- For proxy chains, check the `ForwardLimit` behavior — with `TrustAllProxies=false`, only the last proxy IP is used
- If using multiple proxies, ensure all proxy IPs are listed in `KnownProxies`

### Headers not processed in Kubernetes

**Symptom**: Forward headers work locally but not in Kubernetes, even with headers enabled.

**Cause**: In Kubernetes, the ingress controller or load balancer IP may differ from what's configured in `KnownProxies`.

**Solutions**:

- Use `KnownNetworks` with the pod/service CIDR range instead of specific IPs (e.g. `10.0.0.0/8`)
- Check what IP the request is actually coming from (ingress controller pod IP, node IP, or load balancer IP)
- For development/testing, temporarily enable `TrustAllProxies=true` to confirm headers are the issue

### Verifying header processing

To confirm whether forwarded headers are being processed correctly:

1. Enable verbose logging to see incoming request details
2. Check that `Request.Scheme` shows `https` (not `http`) when accessing via HTTPS-terminating proxy
3. Verify the `Host` header in logs matches the expected public hostname
4. Compare client IP in logs against the expected client IP

