Gryphon Verified Client IP determines the true IP address of visitors to your
WordPress site. When your site sits behind proxies, load balancers, or CDNs,
the standard REMOTE_ADDR server variable contains the proxy's IP rather
than the visitor's. This plugin walks the forwarding header chain and
verifies each hop against your configured trusted proxy networks, stopping
at the first untrusted address — the real client IP.
Or manually upload the gryphon-verified-client-ip folder to
wp-content/plugins/ and activate via the Plugins screen.
Navigate to Settings → Verified Client IP in the WordPress admin.
| Setting | Default | Description |
|---|---|---|
| Enable | On | Master switch. When off, the plugin calculates but does not replace REMOTE_ADDR. Diagnostics still work. |
| Forward Limit | 1 | Maximum number of proxy hops to traverse (1–20). Set this to the number of trusted proxies between your server and the internet. |
| Process Proto | On | When enabled, updates $_SERVER['HTTPS'] and REQUEST_SCHEME from the proxy's forwarded protocol. Originals are preserved in X-Original-HTTPS and X-Original-Request-Scheme. |
| Process Host | Off | When enabled, updates HTTP_HOST and SERVER_NAME from the proxy's forwarded host. Original is preserved in X-Original-Host. |
A scheme defines how the plugin reads forwarding information from a proxy. Three default schemes are provided:
RFC 7239 Forwarded (enabled) — reads the standard Forwarded header's
for token. Trusted proxies default to private/reserved IP ranges.
X-Forwarded-For (enabled) — reads the common X-Forwarded-For header.
Trusted proxies default to private/reserved IP ranges.
Cloudflare (disabled) — reads Cloudflare's CF-Connecting-IP header.
Trusted proxies should be set to
Cloudflare's published IP ranges.
Click Add Scheme at the bottom of the Schemes section. Fill in:
X-Real-IP).Forwarded, the token
to extract (e.g. for).REMOTE_ADDR (the address your server sees).REMOTE_ADDR is not a trusted proxy, the plugin does nothing.The Forward Limit controls how many proxy hops the plugin will traverse. Set this to the exact number of trusted proxies between your server and the internet.
Example: If your stack is Client → Cloudflare → Nginx → WordPress,
you have 2 proxies, so set Forward Limit to 2.
This is a second layer of defense, as even if an attacker works a way around the verification there is a limit on the number of accepted proxies. However note that setting it too low means you'll resolve a proxy's IP instead of the client's, so make sure it is enough to cover your longest proxy chain.
The Diagnostics tab lets you record incoming requests to inspect the headers and algorithm steps without modifying your site's behaviour.
Recording automatically stops when the configured number of requests is reached. Data expires after 24 hours.
Each recorded request shows:
REMOTE_ADDR your server receivedREMOTE_ADDRDiagnostic data contains IP addresses and HTTP headers, which may be personal data under GDPR and similar regulations. Only record what you need, and clear the data as soon as you're done debugging.
vcip_resolved_ip (string $ip, array $steps): string — modify the
resolved IP before it replaces REMOTE_ADDR.vcip_trusted_proxies (array $schemes): array — dynamically add or
modify proxy schemes before matching.vcip_ip_resolved (string $newIp, string $originalIp, array $steps) —
fired after REMOTE_ADDR has been replaced. Useful for logging plugins.For the earliest possible execution, install as a must-use plugin:
gryphon-verified-client-ip.php and the src/ directory to
wp-content/mu-plugins/gryphon-verified-client-ip/.wp-content/mu-plugins/gryphon-verified-client-ip-loader.php:<?php
require_once ABSPATH . 'wp-content/mu-plugins/gryphon-verified-client-ip/gryphon-verified-client-ip.php';
The plugin will run at muplugins_loaded priority 0 instead of
plugins_loaded.
mod_remoteip and nginx set_real_ip_fromBoth Apache and nginx ship with built-in modules that perform IP resolution from forwarding headers:
mod_remoteip reads X-Forwarded-For (or a configured header)
and replaces REMOTE_ADDR before PHP runs.ngx_http_realip_module with set_real_ip_from does the same.When either of these is active and trusts your proxy network, REMOTE_ADDR
will already contain the resolved client IP by the time WordPress (and this
plugin) starts. The plugin will see REMOTE_ADDR as a non-proxy address and
become a no-op.
In the Diagnostics tab, if the Original REMOTE_ADDR column shows the visitor's IP (rather than the direct upstream proxy's IP) even before the plugin has resolved anything, a web-server-level module is pre-resolving the address.
Let this plugin handle all IP resolution. Disable the conflicting module:
Apache — remove or override remoteip.conf:
# /etc/apache2/conf-enabled/remoteip.conf
# Point mod_remoteip at a non-existent header to effectively disable it.
RemoteIPHeader X-No-Such-Header
Or disable the module entirely:
a2dismod remoteip
nginx — remove set_real_ip_from and real_ip_header directives from
your nginx configuration.
If you cannot change the web server configuration, the plugin will still work
for any remaining hops that the web server module did not resolve. For example,
if mod_remoteip resolves one hop and your Forward Limit is 2, the plugin
will resolve the next hop.
However, the plugin's step trace in Diagnostics will start from the already-resolved address, so the full chain will not be visible.
| Problem | Solution |
|---|---|
| Plugin has no effect | Check that REMOTE_ADDR matches a trusted proxy in an enabled scheme. Use Diagnostics to verify. |
| Plugin is a no-op even though proxies are configured | Apache mod_remoteip or nginx set_real_ip_from may be pre-resolving the IP. See Compatibility with Apache mod_remoteip. |
| Wrong IP resolved | Check your Forward Limit matches the number of proxies. Use the step trace in Diagnostics. |
| All requests show proxy IP | The proxy may not be setting the expected header. Check the header name in your scheme matches what the proxy actually sends. |
| Cloudflare not working | Enable the Cloudflare scheme and add Cloudflare's IP ranges to the trusted proxies list. |