Skip to content

Sites Behind Cloudflare Proxy Give Proxy's IP Address in Emails Rather than the User's IP Address #800

Closed as not planned

Description

@alexclst

Describe the bug

In the 0.15.0 update pull request #728 adds the user's IP address to the email message. That code $_SERVER['REMOTE_ADDR'] to find the address. But for sites that sit behind Cloudflare's proxy that variable will return the proxy's IP address, not the user's. You can access the user's IP address from $_SERVER['HTTP_CF_CONNECTING_IP']. This message should be updated to check for that variable first, or filter the found IP address so we can separately add filters to check that variable.

Steps to Reproduce

  1. Run this plugin on a site that sits behind Cloudflare's proxy
  2. Trigger the email message about a login discussed in pull request #728
  3. Compare the IP address the generated email message has to your own
  4. Notice they differ

Screenshots, screen recording, code snippet

No response

Environment information

No response

Please confirm that you have searched existing issues in this repository.

Yes

Please confirm that you have tested with all plugins deactivated except Two-Factor.

Yes

Activity

  1. masteradhoc commented on Feb 17, 2026

    @masteradhoc
    Collaborator

    Hey @alexclst
    WordPress itself uses $_SERVER['REMOTE_ADDR'] quite a lot in their source code. i couldnt find anything related to HTTP_CF_CONNECTING_IP used in Core.

    Comment found here.

    * We use `REMOTE_ADDR` here directly. If you are behind a proxy, you should ensure
    * that it is properly set, such as in wp-config.php, for your environment.
    *
    * See {@link https://core.trac.wordpress.org/ticket/9235}
    

    Would the solution suggested here work for you?
    https://core.trac.wordpress.org/ticket/9235#comment:40
    Cross referencing [15560] as well as this potential snippet, good for wp-config.

    if ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) && preg_match( '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $_SERVER['HTTP_X_FORWARDED_FOR'] ) )
    	$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
    

    Again, specific to your environment. You also may want to check for HTTP_X_FORWARDED_PROTO for HTTPS.

    Please check @kasparsd response on the PR #728 here.

    Secondly, this might return invalid IP if the site is behind a proxy. Unfortunately WP core doesn't have a helper function to retrieve this data consistently. The closest implementation is this https://developer.wordpress.org/reference/classes/wp_community_events/get_unsafe_client_ip/ _but as explained in this article, we should probably stick to $SERVER['REMOTE_ADDR'] and possibly add a filter to adjust as needed.

    Let me know what you think! Else we're open for a PR as well to add a filter.

  2. dd32 commented on Feb 18, 2026

    @dd32
    Member

    Ideally users who are running behind the Cloudflare proxy should use a plugin which validates the cloudflare headers, rather than having WordPress or Two-Factor blindly accept the IP provided. Plugins such as Cloudflares own should do this: https://wordpress.org/plugins/cloudflare/

    For example, with the code in the above example...

    if ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) && preg_match( '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $_SERVER['HTTP_X_FORWARDED_FOR'] ) )
        $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
    

    If a site was NOT running behind cloudflare, curl https://victim.site/ -H 'X-Forwarded-For: 1.2.3.4 would show the invalid IP address in all notifications, and if the Cloudflare header was trusted, same thing for: curl https://victim.site/ -H 'CF-CONNECTING-IP: 1.2.3.4. (Note: It doesnt' support IPv6 which it should)

    Such code CAN be used on a site, if for example, there's a known reverse proxy in front which strips X-Forwarded-For and always inserts the proper data...

  3. alexclst commented on Feb 18, 2026

    @alexclst
    Author

    Thanks for these frankly more appropriate solutions than changes to this plugin.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions