Skip to content

Running Behind Cloudflare or a CDN

sarangshahane edited this page Aug 25, 2026 · 1 revision

Running Behind Cloudflare or a CDN

If your site sits behind Cloudflare, a load balancer, or any reverse proxy, read this before going live. Without a small amount of configuration the widget will work fine in testing and then fail for most visitors once there is traffic.

The problem

The plugin identifies visitors by REMOTE_ADDR, the address PHP sees the request coming from. Behind a proxy, that is the proxy's address, not the visitor's.

Every visitor therefore looks like the same person. The per-IP ceiling of 15 generations per minute stops being a per-visitor protection and becomes a limit on your entire audience. The sixteenth visitor in any given minute sees:

Please wait before requesting another question.

On a quiet site you will never notice. On a site with real traffic the widget looks broken to most people, most of the time.

The fix

The ai_fq_client_ip filter tells the plugin which address to trust. For Cloudflare:

add_filter( 'ai_fq_client_ip', function ( $ip ) {
    if ( ! empty( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) {
        return sanitize_text_field( wp_unslash( $_SERVER['HTTP_CF_CONNECTING_IP'] ) );
    }
    return $ip;
} );

Put it in a small must-use plugin or your theme's functions.php.

Only do this if you are actually behind that proxy

This is the important caveat, and it is why the plugin does not read forwarded headers by default.

HTTP_CF_CONNECTING_IP, X-Forwarded-For and friends are just request headers. Anyone can send them. If your site is not behind Cloudflare — or is reachable directly on its origin address, bypassing it — then trusting that header hands every caller a free way to look like a different visitor on every request, which is exactly the rate-limit bypass the per-IP ceiling exists to prevent.

Only trust a header that a proxy you control is guaranteed to set and overwrite. If visitors can reach your origin directly, lock that down first.

Other proxies

The pattern is the same, with a different header. For a proxy that sets X-Forwarded-For, take the first entry, since the header is a comma-separated chain that downstream clients can prepend to:

add_filter( 'ai_fq_client_ip', function ( $ip ) {
    if ( empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
        return $ip;
    }
    $chain = explode( ',', wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) );
    return sanitize_text_field( trim( $chain[0] ) );
} );

Again: only if your proxy overwrites that header rather than appending to whatever the client sent.

Checking it worked

There is no admin screen for this. The straightforward test is to load a page with the widget from two devices on different networks — a laptop on wifi and a phone on mobile data — and confirm both get questions rather than the wait message.

For a firmer check, temporarily log what the filter returns and confirm you see real visitor addresses rather than a single repeated proxy address. Remove the logging afterwards.

Page caching is fine

If your CDN or a caching plugin serves cached HTML, the widget still works. It fetches its question over a REST call at runtime, so cached markup does not mean a cached question — every visitor gets a fresh one.

One detail if you go looking: the widget token embedded in the page is generated when the HTML is rendered, so all visitors served the same cached page share a token. That is harmless. The token is a per-instance correlation value, not authentication, and the punchline is protected by other means. See How the Punchline Stays Hidden.

Clone this wiki locally