CF-Connecting-IP

The HTTP CF-Connecting-IP request header is an unofficial header carrying the address of the client connecting to Cloudflare. Cloudflare adds the header on the leg from its edge to the origin, so the value reaches the application while the browser never sees the field.

Usage

A proxied request arrives at the origin from a Cloudflare address rather than from the visitor, so REMOTE_ADDR and every log line built from the connection report the edge instead of the person. CF-Connecting-IP restores the missing information.

The header holds exactly one address in every case, which is the practical reason to prefer the header over X-Forwarded-For. A list arriving in X-Forwarded-For needs parsing and a decision about which entry to trust, and a single value needs neither.

Cloudflare recommends reading CF-Connecting-IP rather than X-Forwarded-For for exactly this reason.

Traffic reaching the origin outside the proxy carries no CF-Connecting-IP at all. A DNS-only record resolves to the origin address directly, leaving no edge leg to add the header, and the same applies to anyone connecting to the origin address after discovering the location. Spectrum applications running over plain TCP also leave the header absent, since the field belongs to HTTP rather than to the transport.

Values

The value is a single bare IP address. IPv4 appears in dotted-quad form and IPv6 in ordinary text form without surrounding brackets. Documented examples carry no port.

CF-Connecting-IP: 203.0.113.1
CF-Connecting-IP: 2001:db8::8a2e:370:7334

IPv6 visitors reaching an IPv4 origin

An IPv6 client reaching an origin understanding only IPv4 leaves the origin with an address outside anything the software stores or compares. Cloudflare offers Pseudo IPv4 for the gap, with three settings available on all plans.

Leaving the feature off passes the real IPv6 address through unchanged.

Add Header keeps CF-Connecting-IP on the real IPv6 address and adds CF-Pseudo-IPv4 carrying a Class E IPv4 address hashed from the IPv6 value.

Overwrite Headers replaces CF-Connecting-IP and X-Forwarded-For with the pseudo IPv4 address, and preserves the real address in CF-Connecting-IPv6. Origins needing no code changes take this option.

Class E addresses sit in an experimental range reserved away from production internet traffic, which is what makes them safe to synthesise. Software validating addresses against public ranges rejects them, so a pseudo address occasionally fails a check written for real traffic.

Both companion headers appear only under their own setting. CF-Connecting-IPv6 is not a routine counterpart to CF-Connecting-IP on IPv6 traffic, and CF-Pseudo-IPv4 arrives only where Add Header is active.

True-Client-IP carries an identical value under a different name, enabled through a Cloudflare Managed Transform, and exists for equipment already configured to read the alternate name. Nothing separates the two beyond the naming.

The name is not exclusive to Cloudflare. Akamai emits True-Client-IP as well, so an origin behind a stacked configuration reads whichever proxy wrote last. Cloudflare's own guidance covers the case: an origin authenticating on True-Client-IP sets the header on its own requests, since an unset header accepts any value an attacker supplies.

Cloudflare appends to X-Forwarded-For rather than replacing the value, which is the detail most often stated backwards. A request arriving with no such header reaches the origin carrying the visitor address alone. A request arriving through other proxies reaches the origin with the address of the proxy connecting to Cloudflare appended to whatever came before.

Other vendors solve the same problem under their own names. Fastly sends Fastly-Client-IP, and AWS CloudFront sends CloudFront-Viewer-Address, which carries a port alongside the address and needs splitting before use.

Trusting the header

Any client sets CF-Connecting-IP on a request. An origin reachable on its own address receives whatever an attacker writes, and treating the value as authentic hands over control of rate limiting, geolocation, audit trails, and address-based access rules.

The header earns trust from the connection carrying the value rather than from the value itself. Restricting the origin to Cloudflare connections makes the field meaningful, and Cloudflare documents three approaches with different strength.

Authenticated Origin Pulls verifies a client certificate at the origin, proving the request came through Cloudflare. Available on every plan, and Cloudflare rates the approach highly. Certificate scope matters: the shared global certificate proves only a Cloudflare account sent the request, while a zone-level or per-hostname certificate proves the request came through a specific account.

Allowlisting Cloudflare address ranges at the firewall is simpler and weaker, and Cloudflare describes the approach as moderately secure and vulnerable to address spoofing. The published ranges change, so a static copy in a firewall rule drifts.

Cloudflare Tunnel removes the inbound path entirely by having the origin dial out, which earns the same high rating as Authenticated Origin Pulls.

Current ranges live at cloudflare.com/ips-v4 and cloudflare.com/ips-v6 as plain text, and at api.cloudflare.com/client/v4/ips as JSON needing no authentication. Automating the refresh keeps firewall rules aligned with the published list.

Restoring the visitor IP at the origin

Reading the header inside application code works and leaves server logs still recording Cloudflare addresses. Restoring the address at the server layer fixes logs, rate limiting, and application code together.

nginx

The real IP module rewrites the connection address from a trusted header. Each Cloudflare range needs its own set_real_ip_from line, and the module ignores the header from any other source.

set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
# repeat for every published Cloudflare range

real_ip_header CF-Connecting-IP;

Logging the original address without rewriting the connection uses the header variable directly.

log_format cf '$http_cf_connecting_ip - $remote_user '
              '[$time_local] "$request" $status';

Apache

Apache uses mod_remoteip. The older mod_cloudflare module is deprecated and unsupported from Debian 9 and Ubuntu 18.04 onward.

sudo a2enmod remoteip
RemoteIPHeader CF-Connecting-IP
RemoteIPTrustedProxy 173.245.48.0/20
RemoteIPTrustedProxy 103.21.244.0/22
# repeat for every published Cloudflare range

Access logs continue recording the connection address until the format string changes, so %h becomes %a to log the restored client address.

LogFormat "%a %l %u %t \"%r\" %>s %O" combined

Loading the ranges from a file through RemoteIPTrustedProxyList keeps the configuration short where the full list runs to twenty or more entries.

The trust list is the security control

Both modules apply the header only when the connection arrives from a listed address, which is what turns a spoofable field into a reliable one. Configuring either module with a permissive trust list, or reading the header in application code with no source check at all, reintroduces the original exposure.

Reading the address in a Worker

Workers read the header directly. The request.cf object carries geolocation, ASN, and TLS details about the same visitor and holds no address field, so the header is the only route.

const clientIP = request.headers.get("CF-Connecting-IP");

Subrequests behave differently depending on the destination. A subrequest to another Cloudflare zone arrives carrying a Cloudflare address rather than the visitor address, which is deliberate. A subrequest to an origin outside Cloudflare carries the visitor address as normal.

Example

A visitor on 203.0.113.1 requests a page through Cloudflare. The origin receives the connection from a Cloudflare address while the header carries the visitor.

GET /account HTTP/1.1
Host: www.example.re
CF-Connecting-IP: 203.0.113.1
X-Forwarded-For: 203.0.113.1
CF-Ray: 8a1b2c3d4e5f6789-AMS

The same request arriving through another proxy ahead of Cloudflare carries the proxy's address in CF-Connecting-IP, since the proxy is the client connecting to Cloudflare, while X-Forwarded-For preserves the whole chain.

CF-Connecting-IP: 198.51.100.101
X-Forwarded-For: 203.0.113.1,198.51.100.101

Crawler verification

Origins left unrestored record every request as arriving from a Cloudflare address, which collapses crawler traffic and visitor traffic into the same handful of addresses. Log analysis separating Googlebot from other clients stops working, and address-based rate limiting throttles every client at once rather than per visitor.

Verifying a crawler calls for a reverse DNS lookup on the connecting address, so the check depends on the restored value. Running the lookup against a Cloudflare address returns a Cloudflare hostname and marks genuine Googlebot traffic as unverified.

See also

Last updated: August 18, 2026