How to get real client IP in AWS CloudFront

In the realm of web development, obtaining accurate client IP information is a common challenge, especially when leveraging Content Delivery Networks (CDNs) like AWS CloudFront. The quest for the client’s true IP address often involves navigating through headers like X-Forwarded-For, but it’s not as straightforward as it might seem.

The X-Forwarded-For Header in CloudFront

The official CloudFront documentation outlines the behavior of the X-Forwarded-For header, presenting scenarios where the client’s IP could be at the front, middle, or end of the header. However, a closer inspection reveals that this might not always be the case.

Contrary to some assumptions, CloudFront adheres to the correct semantics for X-Forwarded-For. The header is constructed in a way that each system handling the request appends its client’s address to the right. This implies that the rightmost address in X-Forwarded-For is consistently the address of the machine directly connected to CloudFront.

Trusting the Rightmost Value

It is crucial to emphasize that the rightmost value in X-Forwarded-For, and potentially the only value, is the only one that can be reliably trusted. Any preceding addresses are subject to scrutiny, and their authenticity cannot be guaranteed. This is a fundamental concept in X-Forwarded-For handling that developers often overlook.

Handling Address Trustworthiness

Parsing from the right, any known and trusted addresses can be safely removed. However, the first untrusted address encountered, when reading from right to left, becomes the client’s actual IP address. This meticulous approach is essential, especially in environments where multiple components contribute to the X-Forwarded-For header.

For instance, if CloudFront, an Application Load Balancer (ALB), and a web server are all adding to the X-Forwarded-For header, each component’s contribution must be accounted for. Removing untrusted addresses must be done cautiously, considering the specific addresses introduced by each layer.

Example Scenario

Certainly! Below is a text diagram illustrating the “Example Scenario” with both the “Before X-Forwarded-For” and “After Adding X-Forwarded-For” examples:

Before X-Forwarded-For:

Client Sends:                  CloudFront Adds:              ALB Adds:                    Web Server Adds:

X-Forwarded-For: a, b, c        X-Forwarded-For: a, b, c     X-Forwarded-For: a, b, c      X-Forwarded-For: a, b, c

After Adding X-Forwarded-For:

Consider a scenario where the client sends X-Forwarded-For as “a, b, c,” CloudFront adds “d,” the ALB adds “e,” and the web server adds “f.” Trusting and removing addresses must be executed in a precise order. In this case, removing “f” based on the CIDR range of the balancer subnets, followed by removing “e” within CloudFront address ranges, leaves “d” as the client address.

Client Sends:                  CloudFront Adds:              ALB Adds:                    Web Server Adds:

X-Forwarded-For: a, b, c        X-Forwarded-For: a, b, c, d  X-Forwarded-For: a, b, c, d, e  X-Forwarded-For: a, b, c, d, e, f
                                                                                         
                                                            Trust and Remove:
                                                            - Remove f (CIDR range of balancer subnets)
                                                                                        
                                                                                         
                                                            Trust and Remove:
                                                            - Remove e (CloudFront address ranges)
                                                                                        
                                                                                         
                                                            Trust and Remove:
                                                            - Remove d (Client address)

Lambda@Edge Triggers and Client IP

In Lambda@Edge triggers, CloudFront simplifies the process by providing the client IP address directly in event.Records[0].cf.request.clientIp. Unlike the X-Forwarded-For header, this value is consistently a single address and corresponds to the rightmost value of X-Forwarded-For as the request departs CloudFront for your origin.

Leveraging CloudFront Functions for Simplicity

Following this guide (link), CloudFront introduces Functions, a powerful tool that streamlines the process of adding the true-client-ip header without relying on Lambda@Edge. Here’s a simple example function to demonstrate this capability:

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;
}

With this CloudFront Function, you can seamlessly enhance your request handling by incorporating the true-client-ip header, making it more accessible and eliminating the need for additional Lambda@Edge configurations.

Understanding the intricacies of client IP retrieval in AWS CloudFront is vital for making informed decisions in real-time applications. Developers must grasp the nuances of X-Forwarded-For and tailor their handling mechanisms to ensure the accuracy and security of client IP information in their systems. With the introduction of CloudFront Functions, this process becomes even more straightforward, offering a more streamlined approach to enriching request headers.