Skip to content
OCM LABS

How to get the real visitor IP in Magento behind Cloudflare

Every Magento order shows a Cloudflare IP? Restore the real visitor address from CF-Connecting-IP safely, fix load balancers, and verify it.

Shane Blandford
Shane Blandford · FOUNDERPUBLISHED · 7 MIN READ

Open any order placed since the store went behind Cloudflare and look at the IP it was placed from. It is a Cloudflare address, and so is the one on every other order.

That is the default state of Magento behind any proxy. Cloudflare opens a new connection to your origin, the web server records Cloudflare's address in REMOTE_ADDR, and Magento stores that as the Magento customer IP address on the order. OCM Labs Cloudflare, which is free and open source, fixes it in a few minutes. The interesting part is doing it without opening a spoofing hole. If the module is not installed yet, start with setting up Cloudflare for Magento.

What breaks when Magento only sees Cloudflare

The wrong IP on an order is the visible symptom. The quieter ones cost more:

  • Fraud and velocity checks see one visitor. Rules built on orders-per-IP either fire on everyone or get switched off.
  • Geo-IP features point at a data center, so anything keyed on the visitor's location resolves to wherever Cloudflare's edge sits.
  • Admin IP allowlists cannot be used. Every request arrives from Cloudflare, so the allowlist either blocks everyone or has to be opened to Cloudflare entirely.

How CF-Connecting-IP works, and why trusting it blindly is a mistake

Cloudflare adds the real visitor address to every proxied request in the CF-Connecting-IP header. The naive fix is to tell Magento to read that header instead of REMOTE_ADDR.

The problem: anyone who can reach your origin directly can send a CF-Connecting-IP header with whatever value they like. If Magento trusts it unconditionally, someone pointing curl at your origin IP can claim to be your office range, or a fresh address for every order. The allowlist you were trying to fix becomes the thing they walk through.

The module handles this with a peer check. With Trust CF-Connecting-IP on (the default), it plugs into Magento's remote address resolver and returns the header's value instead of REMOTE_ADDR, but only when the TCP peer, the raw REMOTE_ADDR, is inside Cloudflare's published IP ranges. A request from any other address keeps REMOTE_ADDR, header or not. IPv4-mapped IPv6 peers are recognized, so an origin that listens on a dual-stack socket still passes the check.

The ranges are refreshed daily from Cloudflare by a cron job at 04:30 and stored in the module's ocmlabs_cloudflare cache type. A copy ships with the module as a fallback, so the check works before the first cron run and keeps working if the fetch ever fails. You do not maintain the list by hand.

Step 1: Confirm the setting is on

  1. Go to Stores > Configuration > OCMLabs > Cloudflare > Real IP.
  2. Check Trust CF-Connecting-IP. The default is Yes, so on a fresh install it is already on. The group is default scope; there is nothing to set per website.
  3. Make sure Enable Cloudflare Integration under General is also Yes. It is the kill switch for the whole module, and with it off there is no real IP handling at all.

From the command line or in a deploy script, the path is:

bin/magento config:set ocmlabs_cloudflare/real_ip/trust_cf_connecting_ip 1
bin/magento cache:clean config

What to expect: for any request that arrives from a Cloudflare address, Magento now sees the visitor's real IP everywhere it calls the remote address resolver, so orders and anything else built on the standard resolver pick it up without their own configuration.

Step 2: Run the Real visitor IP health check

The module ships 25 health checks, and one of them exists for this feature. The "Real visitor IP" check inspects the current admin request and reports which of three cases you are in:

PEER WAS CLOUDFLARE, HEADER HONOREDWorking. Orders will carry the real address.
PEER WAS NOT CLOUDFLARE, HEADER IGNOREDSomething between Cloudflare and Magento is rewriting the peer. See step 3.
HEADER MISSINGThe request did not come through Cloudflare at all.

To run it:

  1. Open the admin through the hostname Cloudflare proxies. A direct origin address or a hosts-file override makes the check report on that request instead of a real customer's.
  2. Go to System > Cloudflare > Dashboard > Health and click Run checks now.
  3. Find the "Real visitor IP" row and read the evidence column. It shows what REMOTE_ADDR was and whether CF-Connecting-IP was used.

Note that bin/magento ocmlabs:cloudflare:health skips this check. A CLI run has no Cloudflare request to inspect, so the row shows skip rather than a result. Only an admin request that came through Cloudflare means anything here. For the rest of the checks and how to read them, see running Cloudflare health checks for Magento.

Step 3: Fix the load balancer or proxy case

If the check reports that the peer was not Cloudflare, the usual cause is something sitting between Cloudflare and your web server that does not rewrite REMOTE_ADDR. An AWS Application Load Balancer is the common one; an nginx reverse proxy is another. The peer Magento sees is the proxy, which is not in Cloudflare's ranges, so the module correctly ignores the header.

The fix belongs in the web server, not in Magento. Restore REMOTE_ADDR from the proxy's X-Forwarded-For header, trusting only the proxy's own range. For nginx:

set_real_ip_from 10.0.0.0/16;   # your load balancer or proxy range
real_ip_header X-Forwarded-For;

Replace 10.0.0.0/16 with the range your load balancer actually uses. For Apache, mod_remoteip with RemoteIPInternalProxy set to the same range does the same job.

Once REMOTE_ADDR is Cloudflare's address again, the module takes over and reads CF-Connecting-IP. The two layers compose: the web server strips the load balancer, the module strips Cloudflare.

Reload the web server, then repeat step 2. The health check should now report that the peer was Cloudflare and the header was honored.

Doing this at the web server is worth it anyway. Your access logs and anything else on the host get the right IP too, not just Magento.

Step 4: Place a test order

The health check covers one admin request. An order proves it end to end.

  1. From a device that is not on the same network as the server, open the storefront through Cloudflare and place a test order.
  2. In the admin, open the order and check the IP it was placed from.
  3. Compare it with the public address of the device you ordered from.

If it matches, you are done. If it still shows a Cloudflare address, check that the module is enabled, that bin/magento setup:di:compile has been run since install, and that the order went through the proxied hostname.

When to turn the setting off

Some hosts already restore the real IP in the web server from CF-Connecting-IP itself, with set_real_ip_from listing Cloudflare's ranges and real_ip_header CF-Connecting-IP. If that is your setup, REMOTE_ADDR is already the visitor's address by the time PHP starts, and the module has nothing to do.

Set Trust CF-Connecting-IP to No in that case. The two approaches are equivalent. Doing it twice is harmless but pointless, and one source of truth makes the next person's debugging shorter. If you list Cloudflare's ranges in nginx by hand, you own keeping them current.

The full reference, including the three health check cases, is on the Real Visitor IP docs page.

If your setup has more moving parts than one load balancer, or you want someone to audit the whole path from edge to origin, our performance and infrastructure team does this work.

FAQ

Why do all my Magento orders show the same Cloudflare IP?+
Cloudflare proxies every request, so your web server records a Cloudflare address in REMOTE_ADDR and Magento stores that on the order. The visitor's real address is in the CF-Connecting-IP header, and Magento does not read it unless something tells it to.
Is it safe to trust the CF-Connecting-IP header?+
Only from Cloudflare. Anyone who can reach your origin directly can send the header with any value. The module checks that the TCP peer is inside Cloudflare's published IP ranges before honoring the header, and ignores it from any other address.
Where is the Trust CF-Connecting-IP setting in Magento?+
Stores > Configuration > OCMLabs > Cloudflare > Real IP, field Trust CF-Connecting-IP. It defaults to Yes and is default scope. The config path for bin/magento config:set is ocmlabs_cloudflare/real_ip/trust_cf_connecting_ip.
Why is the Real visitor IP health check skipped from the CLI?+
The check inspects the request it runs in. A CLI run has no HTTP request and no Cloudflare peer to inspect, so it is skipped there and from the hourly cron run. Run it from the admin Health tab after opening the admin through the Cloudflare-proxied hostname.
Does the real IP work behind an AWS Application Load Balancer?+
Not on its own. The peer Magento sees is the load balancer, not Cloudflare, so the module ignores the header. Restore REMOTE_ADDR in nginx with set_real_ip_from and real_ip_header X-Forwarded-For, or in Apache with mod_remoteip, and the module takes over from there.
How does the module keep Cloudflare's IP ranges current?+
A cron job fetches the ranges from Cloudflare daily at 04:30 and stores them in the module's cache type. A copy of the ranges ships with the module as a fallback, so the peer check works before the first cron run and if a fetch fails.
Shane Blandford

Shane Blandford

FOUNDER

Founder of Orange Collar Media, a Denver ecommerce agency behind 800+ Magento and Shopify builds. In Magento since version 1.x - building, rescuing, and supporting revenue-critical stores since 2008.

All posts by Shane Blandford→

KEEP READING

Related from the Journal.

Your store can sell more. Let's find out how much.

Start a project