Does Jetpack Still Improve Performance if you are Already on Cloudflare?

Performance Jet

Short answer: not really — and in some cases Jetpack’s “performance” modules just duplicate what Cloudflare is already doing for you.


Where Jetpack and Cloudflare overlap

Jetpack’s Site Accelerator (formerly Photon)

  • Offloads images & static assets to Automattic’s CDN, converts to WebP, and optionally resizes on the fly.
  • Cloudflare already has a global CDN, can serve WebP, and (on Pro/Business) can resize images on the fly.
  • Result: you’re stacking two CDNs, which adds complexity without much extra speed.

Jetpack’s Lazy Loading for images

  • Cloudflare has Polish + Mirage (Pro+) that already handles lazy loading & optimization.
  • If you enable both, you risk double-handling or odd rendering glitches.

Jetpack’s Video Hosting

  • If you don’t use Jetpack VideoPress, this doesn’t matter — and Cloudflare Stream can fill the same role.

Where Jetpack can still add value (even with Cloudflare)

  • Downtime monitoring — Cloudflare doesn’t do uptime checks unless you add an external monitor.
  • Stats — lightweight visitor analytics if you don’t want Google Analytics.
  • Security scanning / brute force blocklist — different from Cloudflare’s WAF; Jetpack’s is WP-specific.
  • Centralized site management if you run multiple WP installs.

When Jetpack hurts performance on Cloudflare

  • If you enable Site Accelerator and Cloudflare already caches/optimizes, you may add extra DNS lookups and different cache rules.
  • Jetpack’s image CDN sits in front of Cloudflare’s, so Cloudflare sees Jetpack’s hostname as origin, not yours — reducing your cache hit ratio on CF.
  • For some sites, Photon’s resizing is slower than CF’s native resizing because it’s not edge-local.

Bottom line

If you’re already on Cloudflare and just want speed, Jetpack’s performance tools are mostly redundant.
What I usually recommend in your position:

  • Disable Jetpack’s “Performance & Speed” toggles:
    • Site Accelerator (images & static files)
    • Lazy loading (let CF do it if you have Mirage/Polish)
  • Keep Jetpack only for features Cloudflare doesn’t replace (monitoring, stats, auto-updates, maybe security).

If you want, I can give you a Jetpack settings list that’s optimized for “Cloudflare + WordPress” so you keep the useful bits and ditch the dead weight. That would make your forms and site leaner, with fewer moving parts to exploit.

No it is OK. I think I will stick with Contact Forms 7

That’s a good call. Contact Form 7 keeps everything local – no centralised API hop, so your Cloudflare Turnstile actually gets to do its job before anything hits your inbox.

If you’re moving from Jetpack Forms to CF7, here are the quick wins so you don’t re-invite the spam problem:


1. Add Cloudflare Turnstile natively CF7 already has a Turnstile plugin – Contact Form 7 – Cloudflare Turnstile – or you can drop the script and token field in manually. The key is server-side verification before the form sends mail.


2. Honeypot Install the Contact Form 7 Honeypot plugin – trivial to add, very effective against dumb bots.


3. Keyword blocklist CF7 lets you hook into wpcf7_spam or wpcf7_validate to auto-reject if the message contains “graph.org”, “hs=”, “free btc”, etc. Example:

add_filter('wpcf7_spam', function ($spam, $submission) {
    $data = implode(' ', $submission->get_posted_data());
    return $spam || preg_match('/graph\.org|hs=|free\s*btc/i', $data);
}, 10, 2);

4. Disable REST API access to the form endpoint for non-browser traffic Cloudflare WAF rule:

(http.request.method eq "POST") 
and (http.request.uri.path contains "/wp-json/contact-form-7")
and (not http.user_agent contains "Mozilla")

Action Block or Managed Challenge. That stops most direct API spam hits cold.


5. Test with CF caching in place Cloudflare + CF7 works fine, but be sure your form page is excluded from full-page caching – otherwise submissions can get weird.


With that setup, you’ll have local processing + Turnstile + honeypot + keyword kill-switch + WAF – all of which Jetpack’s centralised model was preventing.

Brilliant. Here’s a single-file, no-dependencies CF7 spam shield that does all of this in one hit:

  • Cloudflare Turnstile inject + server-side verify
  • Honeypot field
  • Minimum time-to-submit (bot throttle)
  • Keyword kill-switch (e.g., graph.org, free btc, hs=)
  • Burst rate-limit per IP (simple transient)

Drop it in and you’re done.


Install (2 minutes)

  1. Create a file: wp-content/ÎĽ-plugins/cf7-spam-shield.php (ÎĽ-plugin = always on, no UI)
  2. Paste the code below.
  3. Set your Turnstile keys in the config block at the top.
  4. Load a page with a CF7 form and submit once to test.

<?php
/**
 * Plugin Name: CF7 Spam Shield (Turnstile + Honeypot + TTS + Keywords)
 * Description: Single-file hardening for Contact Form 7: Cloudflare Turnstile (render + verify), honeypot, min submit time, keyword blocks, IP burst throttling.
 * Version:     1.0.0
 * Author:      Simon & ChatGPT
 * License:     GPL-2.0+
 */

/**
 * =========================
 * CONFIG - EDIT THESE
 * =========================
 */
if (!defined('CF7SS_TURNSTILE_SITE_KEY')) {
    define('CF7SS_TURNSTILE_SITE_KEY', 'YOUR_TURNSTILE_SITE_KEY'); // <-- set me
}
if (!defined('CF7SS_TURNSTILE_SECRET_KEY')) {
    define('CF7SS_TURNSTILE_SECRET_KEY', 'YOUR_TURNSTILE_SECRET_KEY'); // <-- set me
}
// Keywords (case-insensitive) that instantly mark as spam if present anywhere in submission:
if (!defined('CF7SS_KEYWORDS')) {
    define('CF7SS_KEYWORDS', json_encode([
        'graph\.org', 'telegraph', 't\.me', 'free\s*btc', 'bitcoin\s*deposit', '\bhs='
    ]));
}
// Minimum milliseconds between form render and submit (bots don't wait):
if (!defined('CF7SS_MIN_SUBMIT_MS')) {
    define('CF7SS_MIN_SUBMIT_MS', 3000);
}
// Rate limit: max submissions per 10 minutes per IP before marking as spam:
if (!defined('CF7SS_RATE_LIMIT_MAX')) {
    define('CF7SS_RATE_LIMIT_MAX', 5);
}

/**
 * Utilities
 */
function cf7ss_client_ip() {
    foreach (['HTTP_CF_CONNECTING_IP','HTTP_X_FORWARDED_FOR','REMOTE_ADDR'] as $k) {
        if (!empty($_SERVER[$k])) {
            $v = $_SERVER[$k];
            if ($k === 'HTTP_X_FORWARDED_FOR') { $v = explode(',', $v)[0]; }
            return trim($v);
        }
    }
    return '0.0.0.0';
}

/**
 * 1) Front-end: inject Turnstile + honeypot + timestamp on CF7 forms
 */
add_action('wp_enqueue_scripts', function () {
    if (is_admin()) return;
    // Turnstile API
    wp_enqueue_script('cf7ss-turnstile', 'https://challenges.cloudflare.com/turnstile/v0/api.js', [], null, true);
    // Our injection
    wp_add_inline_script('cf7ss-turnstile', '
    (function(){
      function ready(fn){ if(document.readyState!="loading"){fn();} else {document.addEventListener("DOMContentLoaded",fn);} }
      ready(function(){
        document.querySelectorAll("form.wpcf7-form").forEach(function(form){
          // Honeypot (visible:none)
          var hp = document.createElement("input"); hp.type="text"; hp.name="website"; hp.style.display="none"; form.appendChild(hp);
          // Timestamp
          var ts = document.createElement("input"); ts.type="hidden"; ts.name="ts"; ts.value=Date.now(); form.appendChild(ts);
          // Turnstile mount + hidden token
          var holder = document.createElement("div"); holder.className="cf-turnstile"; holder.style.margin="8px 0";
          var token = document.createElement("input"); token.type="hidden"; token.name="cf_turnstile_token"; form.appendChild(token);
          var submit = form.querySelector("[type=submit]") || form.lastElementChild;
          form.insertBefore(holder, submit);

          // Render Turnstile
          if (typeof turnstile !== "undefined") {
            var w = turnstile.render(holder, {
              sitekey: "'.esc_js(constant('CF7SS_TURNSTILE_SITE_KEY')).'",
              callback: function(t){ token.value = t || ""; },
              "error-callback": function(){ token.value = ""; },
              "expired-callback": function(){ token.value = ""; }
            });
          }
        });
      });
    })();
    ');
});

/**
 * 2) Server-side: CF7 spam decision
 *    We mark as spam if any of these trip:
 *    - Missing/invalid Turnstile token
 *    - Honeypot filled
 *    - Too fast submission (under CF7SS_MIN_SUBMIT_MS)
 *    - Keyword match
 *    - Rate limit exceeded
 */
add_filter('wpcf7_spam', function ($spam, $submission) {

    if ($spam) return true; // already flagged by someone else

    if (!class_exists('WPCF7_Submission')) return true; // CF7 not present? be conservative
    $sub = WPCF7_Submission::get_instance();
    if (!$sub) return true;
    $data = $sub->get_posted_data();

    // --- 2.1 Honeypot
    $hp = isset($data['website']) && trim((string)$data['website']) !== '';
    if ($hp) return true;

    // --- 2.2 Min submit time
    $now_ms = (int) (microtime(true) * 1000);
    $ts_ms  = isset($data['ts']) ? (int)$data['ts'] : 0;
    if (!$ts_ms || ($now_ms - $ts_ms) < (int) constant('CF7SS_MIN_SUBMIT_MS')) {
        return true;
    }

    // --- 2.3 Keywords
    $flat = strtolower(implode(' ', array_map(function($v){
        if (is_array($v)) return implode(' ', $v);
        return (string)$v;
    }, (array)$data)));
    $keywords = json_decode(constant('CF7SS_KEYWORDS'), true) ?: [];
    if (!empty($keywords)) {
        $rx = "/(" . implode("|", $keywords) . ")/i";
        if (preg_match($rx, $flat)) return true;
    }

    // --- 2.4 Rate limit per IP (10 minutes window)
    $ip = cf7ss_client_ip();
    $key = 'cf7ss_rate_' . md5($ip);
    $count = (int) get_transient($key);
    $count++;
    if ($count === 1) {
        set_transient($key, 1, 10 * MINUTE_IN_SECONDS);
    } else {
        set_transient($key, $count, 10 * MINUTE_IN_SECONDS);
    }
    if ($count > (int) constant('CF7SS_RATE_LIMIT_MAX')) {
        return true;
    }

    // --- 2.5 Turnstile verification
    $token_fields = ['cf_turnstile_token', 'cf-turnstile-response']; // support both names
    $token = '';
    foreach ($token_fields as $tf) {
        if (!empty($data[$tf])) { $token = sanitize_text_field((string)$data[$tf]); break; }
    }
    if (empty($token)) return true; // no token => spam

    $secret = constant('CF7SS_TURNSTILE_SECRET_KEY');
    if (!$secret || $secret === "YOUR_TURNSTILE_SECRET_KEY") return true; // misconfigured

    $resp = wp_remote_post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
        'timeout' => 5,
        'body'    => [
            'secret'   => $secret,
            'response' => $token,
            'remoteip' => $ip,
        ],
    ]);
    if (is_wp_error($resp)) return true;

    $json = json_decode(wp_remote_retrieve_body($resp), true);
    $ok = !empty($json['success']);
    if (!$ok) return true;

    return false; // passed all checks
}, 10, 2);

/**
 * 3) Optional: nicer feedback when blocked
 */
add_filter('wpcf7_submission_result', function ($result, $class, $contact_form) {
    if (!empty($result['status']) && $result['status'] === 'spam') {
        $result['message'] = __('Your message looked automated and was blocked. If this is an error, please wait a few seconds and try again without links to suspicious domains.', 'cf7ss');
    }
    return $result;
}, 10, 3);

Notes, gotchas, forward-thinking tweaks

  • Keys in code vs wp-config.php: for cleaner ops, you can put: define('CF7SS_TURNSTILE_SITE_KEY', 'xxx'); define('CF7SS_TURNSTILE_SECRET_KEY', 'yyy'); in wp-config.php and remove them from the plugin block.
  • Keyword list: expand as you see patterns (Jetpack/Telegraph spam loves graph.org, hs=, “deposit”, “KYC”, “unlock”, “bonus”).
  • Caching: exclude your contact page from any full-page cache rules to avoid stale tokens.
  • WAF companion: keep your Cloudflare POST-body rule for belt-and-braces protection (managed challenge on graph.org|hs=|free btc in body).
  • Troubleshooting checklist:
    • Token missing? Inspect form: hidden cf_turnstile_token should populate after Turnstile shows.
    • Submissions always blocked? Check your secret key is set and correct; watch for server time offsets if your host is exotic.
    • False positives on timing: nudge CF7SS_MIN_SUBMIT_MS to 1500-2000 if your audience is impatient.

You’ve just traded centralised fragility for local, verifiable gatekeeping. Clean, fast, and under your control – the way forms should be.

Love it. I built Cloudstile as a real, installable plugin — turnkey and tidy.

Download Cloudstile 1.0.0 (ZIP)

What it does (all local, no Jetpack dependency)

  • Injects Cloudflare Turnstile into CF7 forms and verifies server-side.
  • Adds a honeypot field.
  • Enforces minimum time-to-submit (bots hate waiting).
  • Keyword kill-switch (regex; preloaded with graph\.org, hs=, free\s*btc, etc.).
  • Simple IP burst rate-limit (per 10 minutes).

How to use

  1. Install the ZIP in WordPress → Plugins → Add New → Upload.
  2. Activate Cloudstile.
  3. Go to Settings → Cloudstile and paste your Turnstile site + secret keys.
  4. Optional: tweak keywords, min submit time (defaults to 3000 ms), and rate limit.
  5. Make sure your contact page isn’t fully cached at the edge.

That’s it—CF7 + Cloudflare + local verification, working together instead of getting in each other’s way. If you want me to add log views or per-form overrides next, say the word.

Share this content:

Leave a Reply

Your email address will not be published. Required fields are marked *