# WordPress behind Cloudflare or a proxy: get the visitor's real IP

How BetterShield tells visitors apart behind Cloudflare, a load balancer or a proxy, so sign-in pauses follow one person and never everyone at once.

By M Asif Rahman, Oct 23, 2026. Updated Oct 26, 2026.

To get the real visitor IP in WordPress behind Cloudflare or a proxy, tell **BetterShield** which proxy to believe, so it can read the visitor's address from the header the proxy adds instead of seeing every request arrive from one address. Without that, sign-in pauses either stand down or catch everyone at once. This guide covers how BetterShield reads each kind of request, how to set it up, and how to check it worked.

It builds on [limiting login attempts](/blog/limit-login-attempts-wordpress/), which explains the pauses that depend on this reading. If a wrong setting ever catches your own team, [getting back in](/blog/locked-out-of-wordpress/) is the post to open.

## Quick summary

- Sign-in pauses, the puzzle and **Never allowed to sign in** entries act only on an address BetterShield can verify.
- Hosted directly, there is nothing to set: the connecting address is used and forwarding headers are ignored.
- Behind Cloudflare, press **Yes, this site is behind Cloudflare** when the card asks, and only if it is true.
- Behind a load balancer or a host's proxy, name its own addresses under **Trusted proxies** and make sure it sends X-Forwarded-For.
- Where BetterShield cannot tell visitors apart, failed sign-ins are still recorded but nobody is paused, and public forms never close.

## Why the visitor's address matters

BetterShield stops password guessing by pausing the connection that keeps getting it wrong. That only works if "the connection" means one visitor. Behind a reverse proxy, a load balancer or a CDN, every request can arrive from the same address: the proxy's. Count that address and a few mistyped passwords from one person would pause every visitor, you included.

So BetterShield does not simply trust whatever address a request claims. A forwarding header is just text that anyone can send, and believing it from a stranger would let a guesser pick a new address on every attempt. Instead it has to be told whose word to take, which is what the setup below is for.

## How BetterShield reads each request

The doc for this feature sets out five cases. In plain terms:

| How the request arrives | Whose address is used | Pauses |
|---|---|---|
| Straight from a public address | The connecting address. Forwarding headers are ignored, so nobody can choose the address they appear to come from. | Run |
| From Cloudflare’s published address ranges, with Cloudflare’s visitor header | The visitor address Cloudflare names. Until you confirm the site is behind Cloudflare, a pause counts that address and the Cloudflare address together. | Run |
| From a proxy you named under **Trusted proxies**, with an X-Forwarded-For header | The first address in X-Forwarded-For, read from the right, that is not private, not a named proxy and not Cloudflare. | Run |
| From a named proxy that sends no X-Forwarded-For | Nothing names the visitor. | Stopped |
| From a private address that nothing names | Nothing it can trust. | Stopped |

The [X-Forwarded-For header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For) is the common way a proxy passes the original address along, and Cloudflare documents the [headers it adds](https://developers.cloudflare.com/fundamentals/reference/http-headers/) in its own reference. Cloudflare is recognized by its published address ranges, which ship with the plugin and are never fetched, so it needs no **Trusted proxies** entry, only your confirmation.

One case is easy to miss. A proxy or CDN that reaches the site from a public address and is not named falls in the first row: its address is taken as the visitor’s, so one pause would shut out everyone behind it. Name it under **Trusted proxies** and have it send X-Forwarded-For.

When pauses are stopped, failed sign-ins are still recorded, but nobody is paused, nobody gets the puzzle, address entries on **Never allowed to sign in** stand down, and public forms never close. That is the cautious choice: counting the proxy’s address would put every visitor under one pause, and believing an unvouched header would let anyone dodge one.

### Set it up

1. Open **BetterShield › Protection › Login & Access** and read **How this site sees you**. It shows the address you connect from, and what Cloudflare says when it is in front.
2. If it says automatic lockouts are active and asks nothing else, there is nothing to do.
3. If it asks whether the site is behind Cloudflare and it is, press **Yes, this site is behind Cloudflare**. Each pause then follows one visitor. This saves at once.
4. If it says lockouts are not running and you have a reverse proxy or load balancer, type its address or range under **Trusted proxies**, in **Addresses or ranges, separated by commas** (for example 192.0.2.10, 192.0.2.0/24), then press **Save**. A mistyped entry is named before you save.
5. Press **Check again**. If it now says your named proxy sends no forwarding header, set the proxy to send X-Forwarded-For with the visitor’s address.

| Option | What it does | Default |
|---|---|---|
| **Trusted proxies** | Your own proxies or load balancers, separated by commas. Up to 500 entries, each no wider than /8 (IPv4) or /32 (IPv6). | Empty |
| **Yes, this site is behind Cloudflare** | Lets Cloudflare’s visitor header name the visitor on its own. | Not confirmed |
| **Turn that off** | Withdraws the Cloudflare confirmation. Shown when a request did not arrive through Cloudflare although the site trusts it. | Not shown |
| **Check again** | Reads your request again. Neither address is stored. | |

Quick Setup asks the same Cloudflare question and sets the same answer. An entry in X-Forwarded-For that names no address, such as “unknown”, ends the reading and that request is treated as unverified; entries written with a port are read correctly. See [Proxies and Cloudflare](/docs/proxies-and-cloudflare/) for the full reference.

## What it can break, and how to undo

- **A range you do not control.** Any machine inside a trusted range can say who it is carrying, and so dodge pauses. Name only your own infrastructure. Remove the entry and press **Save**.
- **Confirming Cloudflare when the site is not behind it.** Anything inside Cloudflare’s ranges could then name any visitor. The card warns when a request did not come through Cloudflare; press **Turn that off**.
- **Naming a proxy that sends no header.** Pauses stay stopped until it sends X-Forwarded-For.

Adding a proxy and confirming Cloudflare give something away, so with password confirmation on they ask for your password. Each change is recorded as “Login protection changed”.

## Check it is working

- **The summary strip.** **Connections** reads **Verified** (“Read directly” or “Through a proxy”), or **Not verified** with “Lockouts are paused”.
- **The card.** It says connections are identified directly, through Cloudflare, or through the proxy you named, and that automatic lockouts are active.
- **Findings.** **Failed sign-ins are not being counted, so password guessing is not stopped** (High) appears when your own dashboard visits could not be told apart, with **Open Login & Access** to fix it. It keeps the worst it has seen, and starts over when you change **Trusted proxies** or the Cloudflare answer. After a fix, open any BetterShield screen, then run the audit again.
- **Networks elsewhere.** Sessions, the activity log and the new-network email use the same reading, so once it is right they show your visitors’ networks rather than your proxy’s.

Nothing about your address is stored by the card: it reads the request you just made and shows it to you.

## Recommended settings

We recommend reading this card first on every new site. It takes a minute and decides whether pauses work at all.

| Site | What we recommend |
|---|---|
| Hosted directly | Nothing to set. |
| Behind Cloudflare | Press **Yes, this site is behind Cloudflare** when the card asks, and only if it is true. |
| Behind a load balancer or a host’s proxy | Name only that proxy’s own addresses, as narrowly as you can, and make sure it sends X-Forwarded-For. Your host can tell you both. |
| Cloudflare in front of a load balancer | Name the load balancer. The Cloudflare hop is recognized on its own; then press **Yes, this site is behind Cloudflare**. |
| Agency or many sites | Check the card on every new site before relying on lockouts. |

## Frequently asked questions

### Why do all my visitors share one IP address in WordPress?

Because a reverse proxy, load balancer or CDN sits in front of the site, so every request arrives from the proxy’s address. Tell BetterShield about the proxy, or confirm Cloudflare, and it reads the visitor’s own address from the header instead.

### Does BetterShield trust the X-Forwarded-For header?

Only from a proxy you named under **Trusted proxies**. From any other source the header is ignored, so nobody can choose the address they appear to come from.

### Do I have to enter Cloudflare’s IP ranges?

No. Cloudflare’s published ranges ship with the plugin and are never fetched. You only press **Yes, this site is behind Cloudflare**, and only if the site really is.

### What happens if BetterShield cannot tell visitors apart?

Failed sign-ins are still recorded, but nobody is paused, nobody gets the puzzle, and public forms never close. A **Failed sign-ins are not being counted, so password guessing is not stopped** finding appears with **Open Login & Access** to fix it.

### Is this the same as the Ultra Cloudflare controls?

No. This setting is free and only decides whose address to believe. With Ultra, **BetterShield › Ultra › Cloudflare** can also put a challenge on the network of a sign-in lockout at Cloudflare’s edge, which is a separate feature.

## Conclusion

Sign-in pauses are only as good as the address behind them. Read the card, confirm Cloudflare only if it is true, name your own proxy as narrowly as you can, and check that **Connections** reads **Verified**. BetterShield is free on [WordPress.org](https://wordpress.org/plugins/bettershield/); the full reference is [Proxies and Cloudflare](/docs/proxies-and-cloudflare/), and the pauses it feeds are in [Sign-in lockouts](/docs/sign-in-lockouts/).
