When you use an Application Load Balancer (ALB) as a CloudFront origin, the ALB sees CloudFront edge server IPs as the client — not the actual viewer's IP. This article explains three methods to retrieve the real client IP at your ALB target.
Background
In a Client → CloudFront → ALB → Target architecture:
- The ALB receives requests from CloudFront edge servers, not directly from the end user.
- In ALB access logs, the
client:port field contains the CloudFront edge server IP.
- To identify the real viewer IP, you need to use specific HTTP headers.
Methods to Retrieve the Real Client IP
1. X-Forwarded-For Header
X-Forwarded-For is the de facto standard header for identifying the originating client IP. When a request passes through multiple proxies, each proxy appends its IP:
X-Forwarded-For: <client>, <proxy1>, <proxy2>
The leftmost value is the original client IP.
ALB X-Forwarded-For behavior:
ALB supports three modes for this header, configurable via load balancer attributes:
| Mode | Behavior |
|---|
| Append (default) | ALB appends its own node IP to the existing header |
| Preserve | ALB passes the header through without modification |
| Remove | ALB strips the header before forwarding to the target |
Note: ALB also supports client port preservation via the routing.http.xff_client_port.enabled attribute. When enabled, the client port is appended to the IP in X-Forwarded-For (e.g., 130.176.96.72:8080). For IPv6, the address is enclosed in square brackets (e.g., [2600:9000:a518:2d8c::1]:8080).
Note: In most CloudFront + ALB setups, the default Append mode is appropriate. The X-Forwarded-For header will contain: <viewer IP>, <CloudFront edge IP>.
2. CloudFront-Viewer-Address Header
CloudFront can add the CloudFront-Viewer-Address header, which contains the viewer's IP and source port:
CloudFront-Viewer-Address: <client_ip>:<port>
This is available at no additional cost.
How to enable:
- Option A: In your CloudFront distribution's origin request policy, use the managed policy:
AllViewerAndCloudFrontHeaders-2022-06
- Option B: Create a custom origin request policy and add
CloudFront-Viewer-Address under "Include the following headers"
3. True-Client-IP Header (via CloudFront Functions)
You can use a CloudFront Function to set a True-Client-IP header from the viewer request event:
async function handler(event) {
var request = event.request;
var clientIP = event.viewer.ip;
//Add the true-client-ip header to the incoming request
request.headers['true-client-ip'] = {value: clientIP};
return request;
}
Note: This example uses CloudFront Functions JavaScript runtime 2.0, which supports async handlers.
Requirements:
- Associate this function as a viewer request function on your CloudFront distribution.
- Ensure your origin request policy forwards the
True-Client-IP header. The managed policy AllViewerAndCloudFrontHeaders-2022-06 includes it.
Recommendation
- Use
CloudFront-Viewer-Address for the most reliable single-value client IP — it's set by CloudFront itself and not subject to spoofing via the request chain.
- Use
X-Forwarded-For if your application already parses this standard header, but be aware it can be spoofed by the client prepending arbitrary values.
- Use
True-Client-IP via CloudFront Functions if you need a clean, single-value header and want explicit control.
References