Skip to main content
ZenCore Digital
Troubleshooting 9 min read

10 Common WordPress Plugin Conflicts and How to Fix Them

Plugins fighting each other is the #1 cause of WordPress issues. Here are the most common conflicts we see and how to sort them out.

ZenCore Digital

Plugins Break WordPress More Than Anything Else

Every week we get tickets from site owners who updated a plugin and suddenly their site is broken. Forms stopped sending, pages look garbled, checkout is throwing errors, or the whole site is just white.

The cause is almost always the same: two plugins trying to do the same thing, load the same library, or modify the same piece of WordPress in incompatible ways. That’s a plugin conflict.

The tricky part is that both plugins might work perfectly on their own. The conflict only surfaces when they’re active together, which makes diagnosis frustrating if you don’t know where to look.

Here are the ten most common conflicts we deal with, what causes each one, and exactly how to fix them.

1. Caching Plugin + Page Builder (Elementor, Divi, WPBakery)

What happens

You edit a page in Elementor or Divi, hit publish, and the frontend still shows the old version. Or worse — the page builder’s visual editor loads a cached version of the page and the drag-and-drop interface breaks completely. Buttons don’t respond, widgets won’t drag, and the editor toolbar disappears.

Why it happens

Page builders generate dynamic HTML on the fly using PHP and JavaScript. Caching plugins (WP Super Cache, W3 Total Cache, WP Rocket, LiteSpeed Cache) serve a static HTML snapshot of the page instead of running PHP. When the caching plugin serves a cached version inside the builder’s editor iframe, the JavaScript that powers the editor never initializes properly.

Additionally, some caching plugins minify and combine JavaScript files. Page builders rely on specific script loading order — combine those scripts and the dependency chain breaks.

How to fix it

Every major caching plugin has an option to exclude specific pages or URL patterns. You need to exclude the page builder’s editor URLs.

For WP Rocket, add this to your wp-config.php or use the settings panel to exclude URLs containing elementor or et_fb:

// Exclude Elementor editor from WP Rocket cache
add_filter('rocket_cache_reject_uri', function($uris) {
    $uris[] = '(.*)elementor(.*)';
    $uris[] = '(.*)et_fb(.*)';
    return $uris;
});

For W3 Total Cache, go to Performance > Page Cache > Never cache the following pages, and add:

elementor
et_fb=1

Disable JavaScript minification and combination for page builder scripts. In WP Rocket, go to File Optimization > JavaScript Files, and add these to the exclusion list:

/wp-content/plugins/elementor/
/wp-content/themes/Divi/

After making changes, purge the entire cache:

wp cache flush
wp rocket clean --confirm  # WP Rocket
wp w3-total-cache flush all  # W3 Total Cache

2. Yoast SEO vs Rank Math (Dual SEO Plugin Conflict)

What happens

Duplicate meta tags in your page source. You’ll see two sets of <meta name="description"> tags, two canonical URLs, double Open Graph tags, and conflicting XML sitemaps. Google Search Console starts showing warnings about duplicate metadata, and your search rankings drop because Google doesn’t know which metadata to trust.

Why it happens

Both Yoast and Rank Math hook into wp_head to output SEO meta tags. If both are active — even if one was “deactivated” but its data persists — they’ll both try to output metadata. We see this most often when a site migrates from Yoast to Rank Math. The owner installs Rank Math and activates it, but Yoast’s data still exists in the database and sometimes Yoast doesn’t fully deactivate if there’s a file permission issue.

Some theme frameworks (like Genesis or Jesuspended flavors of theme frameworks) also output their own SEO meta tags, creating a three-way conflict.

How to fix it

Pick one SEO plugin and fully remove the other. Not just deactivate — delete it.

# Check which SEO plugins are active
wp plugin list --status=active --fields=name | grep -i -E "seo|rank"

# If migrating from Yoast to Rank Math, Rank Math has a built-in importer
# Run the import FIRST, then deactivate Yoast
wp plugin deactivate wordpress-seo
wp plugin delete wordpress-seo

After removing the duplicate, verify your page source only has one set of meta tags:

curl -s https://yoursite.com | grep -i '<meta name="description"' | wc -l
# Should return 1

If your theme also outputs SEO tags, disable them. In Genesis:

// functions.php — disable Genesis SEO
remove_action('wp_head', 'genesis_seo_site_title', 0);
remove_action('wp_head', 'genesis_meta_description', 0);

3. WooCommerce + Full-Page Caching

What happens

Customers add items to their cart but the cart page shows zero items. Or a customer logs in and sees another customer’s account page. The checkout page serves stale nonce tokens, causing “session expired” errors when submitting payment. This is genuinely dangerous — serving one customer’s account data to another is a privacy violation.

Why it happens

Full-page caching treats every page as static. But WooCommerce pages — cart, checkout, my-account — are dynamic per user. The cart contents are stored in a WooCommerce session tied to a cookie, and the checkout page generates a fresh nonce on every load for security. A cached version of the checkout page has an expired nonce, and a cached cart page shows whatever was in the cart of the first person who loaded it.

How to fix it

Exclude WooCommerce pages and cookies from caching. WP Rocket and LiteSpeed Cache handle this automatically when they detect WooCommerce, but W3 Total Cache and some server-level caches (Varnish, Nginx FastCGI) don’t.

For Nginx FastCGI cache, add to your Nginx config:

# Skip cache for WooCommerce pages and logged-in users
set $skip_cache 0;

if ($request_uri ~* "/cart/*$|/checkout/*$|/my-account/*$|addtocart|add-to-cart") {
    set $skip_cache 1;
}

if ($http_cookie ~* "woocommerce_items_in_cart|woocommerce_cart_hash|wp_woocommerce_session") {
    set $skip_cache 1;
}

For Varnish, add WooCommerce cookies to your VCL:

sub vcl_recv {
    if (req.url ~ "(cart|checkout|my-account)" || req.http.Cookie ~ "woocommerce_items_in_cart") {
        return (pass);
    }
}

Verify it’s working by adding a product to the cart in a private browser window and checking that the cart count updates. Then test checkout with a test payment.

# Flush cache after config changes
wp cache flush
wp wc tool run clear_transients --user=1

4. Security Plugin + Login/Admin Access

What happens

You get locked out of wp-admin entirely. The login page returns a 403 Forbidden, redirects to a blank page, or throws a “too many redirects” error. Sometimes only specific users are affected, and sometimes the lockout happens after a WordPress core update.

Why it happens

Security plugins like Wordfence, Sucuri, iThemes Security, and All-In-One WP Security modify the login process heavily. They change the login URL, add firewall rules to .htaccess, implement IP-based blocking, and rate-limit login attempts. When WordPress core updates and changes login-related behavior, the security plugin’s rules can conflict. Or the plugin’s brute-force protection triggers incorrectly and blocks your own IP.

iThemes Security’s “Hide Login” feature rewrites .htaccess rules. If another plugin (or a WordPress update) also modifies .htaccess, the rules can conflict and make wp-login.php inaccessible.

How to fix it

If you’re locked out and have FTP or SSH access, disable the security plugin at the file level:

# Rename the plugin folder to deactivate it
mv wp-content/plugins/wordfence wp-content/plugins/wordfence_disabled

# Or via WP-CLI
wp plugin deactivate wordfence

If Wordfence’s firewall is blocking you at the server level, you also need to remove its .htaccess rules:

# Back up .htaccess first
cp .htaccess .htaccess.bak

# Remove Wordfence firewall rules — look for lines between
# BEGIN Wordfence WAF and END Wordfence WAF and remove them

For iThemes Security lockouts, the IP blocklist is stored in the database:

# Clear all lockouts
wp db query "DELETE FROM wp_itsec_lockouts WHERE 1=1;"
wp db query "DELETE FROM wp_itsec_temp WHERE 1=1;"

After regaining access, reconfigure the security plugin’s settings. Whitelist your own IP address and any office IP ranges. If you use a custom login URL, write it down somewhere accessible that isn’t your WordPress dashboard.

5. Contact Form Plugin + Caching (AJAX/Nonce Issues)

What happens

Contact form submissions fail silently — the user clicks submit, sees a spinner that never finishes, and the form data vanishes. Or the form returns a generic “There was an error” message. The form worked yesterday, and you haven’t changed anything. Email logs show no attempt was ever made to send the message.

Why it happens

Contact Form 7, WPForms, Gravity Forms, and Formidable all use WordPress nonces (number-used-once security tokens) to verify form submissions. A nonce is valid for 24 hours. When a caching plugin serves a page from cache, the nonce embedded in the form HTML is the one generated when the page was first cached — which could be days or weeks old. An expired nonce means WordPress rejects the form submission as a potential CSRF attack.

This specifically affects full-page caching. Object caching (Redis, Memcached) doesn’t cause this issue.

How to fix it

The cleanest fix is to load nonces via AJAX instead of embedding them in cached HTML. Contact Form 7 does this automatically since version 5.4 — but only if you have REST API access enabled. Check that nothing is blocking wp-json:

curl -s https://yoursite.com/wp-json/contact-form-7/v1/ | head -20
# Should return JSON, not a 403 or 404

If the REST API is blocked (some security plugins do this), whitelist the CF7 endpoint:

// Allow CF7 REST API through security plugin restrictions
add_filter('rest_authentication_errors', function($result) {
    if (strpos($_SERVER['REQUEST_URI'], 'contact-form-7') !== false) {
        return true;
    }
    return $result;
});

For WP Rocket specifically, you can exclude pages with forms from the cache, but a better approach is to enable the “Separate cache for mobile devices” option and make sure AJAX requests aren’t being cached:

// Exclude AJAX handlers from cache
add_filter('rocket_cache_reject_uri', function($uris) {
    $uris[] = '/wp-admin/admin-ajax\.php';
    return $uris;
});

For WPForms and Gravity Forms, both have built-in AJAX submission modes. Enable AJAX submission in the form settings — this bypasses the cached nonce problem entirely because the form fetches a fresh nonce via JavaScript before submitting.

6. Multiple Image Optimization Plugins

What happens

Images on the site look blurry, overly compressed, or display strange artifacts. Upload times become extremely slow — a 2MB image takes 30 seconds to upload instead of two. The server’s CPU usage spikes every time someone uploads media. In extreme cases, the wp-content/uploads directory balloons in size because multiple plugins are creating their own optimized copies.

Why it happens

Running ShortPixel alongside Imagify, or Smush alongside EWWW Image Optimizer, means every uploaded image gets processed twice (or more). Each plugin hooks into wp_handle_upload or wp_generate_attachment_metadata and processes the image independently. The second plugin compresses an already-compressed image, which either degrades quality further or actually increases file size (recompressing a JPEG can make it larger if the quality setting is higher than the first pass).

Some plugins also generate WebP or AVIF conversions. Two plugins generating WebP versions of the same image creates duplicate files and conflicting rewrite rules for serving them.

How to fix it

Pick one image optimization plugin and remove the others completely:

# List all image-related plugins
wp plugin list --status=active --fields=name,title | grep -i -E "image|optim|smush|shortpixel|imagify|ewww|compress"

# Keep your preferred one, deactivate and delete the rest
wp plugin deactivate ewww-image-optimizer
wp plugin delete ewww-image-optimizer

If images have been double-compressed and look bad, you’ll need to regenerate thumbnails from the original uploads:

# Regenerate all thumbnails from originals
wp media regenerate --yes

This only works if the originals are still intact. Most optimization plugins keep the originals by default (check their settings). If originals were overwritten, you’ll need to restore from a backup or re-upload the affected images.

Clean up orphaned WebP files left behind by the removed plugin:

# Find WebP files in uploads directory
find wp-content/uploads -name "*.webp" -type f | wc -l

# If you want to remove them (your remaining plugin will regenerate its own)
find wp-content/uploads -name "*.webp" -type f -delete

7. jQuery Version Conflicts (Plugin Loads Its Own jQuery)

What happens

JavaScript-dependent features stop working across the site. Sliders don’t slide, accordions don’t expand, modals won’t open, and AJAX-powered features fail. The browser console shows errors like $ is not a function, jQuery is not defined, or Cannot read property 'fn' of undefined. Sometimes only specific pages are affected.

Why it happens

WordPress ships with its own version of jQuery and registers it as a script dependency. Well-coded plugins use wp_enqueue_script with 'jquery' as a dependency, which uses WordPress’s bundled copy. Poorly coded plugins — especially older slider plugins, social media widgets, or plugins ported from non-WordPress origins — bundle their own jQuery version and load it directly with a <script> tag.

When two different jQuery versions load on the same page, the second one overwrites window.jQuery and window.$. Any plugin that initialized with the first jQuery version now has broken references. WordPress 6.x ships jQuery 3.7.x, but some plugins bundle jQuery 1.x or 2.x, which have a fundamentally different API.

How to fix it

First, identify which plugins are loading their own jQuery. Open your page source and search for jquery.min.js or jquery.js — you should only see it loaded once from /wp-includes/js/jquery/:

curl -s https://yoursite.com | grep -i "jquery" | grep "<script"

If you see jQuery loaded from a plugin’s directory, that plugin is the offender. The proper fix is to dequeue the plugin’s jQuery and force it to use WordPress’s version:

// functions.php — force plugins to use WP's jQuery
add_action('wp_enqueue_scripts', function() {
    // Dequeue any jQuery loaded by plugins outside of WordPress core
    if (!is_admin()) {
        wp_deregister_script('jquery');
        wp_register_script('jquery', includes_url('/js/jquery/jquery.min.js'), array(), null, true);
        wp_enqueue_script('jquery');
    }
}, 100);

The 100 priority ensures this runs after most plugins have enqueued their scripts. If a specific plugin hardcodes a <script> tag in its template instead of using wp_enqueue_script, you’ll need to remove it with an output buffer or replace the plugin entirely. A plugin that loads jQuery outside the WordPress dependency system in 2026 is a plugin you shouldn’t be using.

For jQuery Migrate warnings (common after WordPress 5.5+ removed jQuery Migrate), install the Enable jQuery Migrate Helper plugin temporarily to identify deprecated jQuery calls, then update or replace the plugins using them.

8. Multiple Security Plugins Running Simultaneously

What happens

The site becomes painfully slow — 10+ second load times on every page. The server CPU is maxed out. Login pages return 403 errors randomly. Legitimate users and even Googlebot get blocked. The .htaccess file grows to hundreds of lines with overlapping rules that contradict each other.

Why it happens

Running Wordfence alongside Sucuri, or iThemes Security alongside All-In-One WP Security, means every HTTP request passes through multiple firewall layers, each running its own full set of security checks. Each plugin scans every request against its own malware signatures, checks its own IP blocklist, validates its own set of rules, and logs its own audit trail. That’s double the database queries, double the file scans, and double the memory usage per request.

The firewall rules also conflict directly. One plugin might allow a request that another blocks. Or both write to .htaccess and one overwrites the other’s rules. We’ve seen cases where Wordfence blocks Sucuri’s scanner, and Sucuri blocks Wordfence’s update requests — each plugin treating the other’s activity as suspicious.

How to fix it

You only need one security plugin. Pick whichever one you prefer and remove the others:

# See all security-related plugins
wp plugin list --status=active --fields=name | grep -i -E "security|wordfence|sucuri|ithemes|shield|defender"

# Deactivate and remove extras
wp plugin deactivate sucuri-scanner
wp plugin delete sucuri-scanner

After removing the extra security plugin, clean up the .htaccess file. Back it up first, then remove the leftover rules:

cp .htaccess .htaccess.bak

Open .htaccess and remove any blocks marked with the removed plugin’s name (they’ll be between comment markers like # BEGIN Sucuri and # END Sucuri). Then regenerate the remaining plugin’s rules from its settings page.

Flush any firewall or object caches:

wp cache flush
wp transient delete --all

If you want layered security without plugin conflicts, use one WordPress-level plugin (like Wordfence) combined with a server-level or DNS-level WAF (like Cloudflare’s WAF or Sucuri’s cloud proxy). These operate at different layers and don’t interfere with each other.

9. Multisite Plugin Activation Conflicts

What happens

A plugin works fine when activated on individual subsites but crashes the entire network when network-activated. Or the reverse — a plugin activated per-site on a multisite network causes database table errors, cross-site data bleed, or broken admin menus on other subsites. Some plugins create database tables on activation, and network activation creates duplicate table creation attempts that fail silently.

Why it happens

Not all WordPress plugins are multisite-compatible. When a plugin is network-activated, WordPress runs its activation hook once — not once per site. Plugins that create custom database tables often use $wpdb->prefix to name their tables, which on multisite includes the site ID (e.g., wp_2_custom_table). A plugin that doesn’t account for multisite will create tables only for the main site and then throw database errors on subsites.

Some plugins store options using get_option() which is site-specific, but their admin menu is registered globally. The settings page works on the site where it was first configured but shows blank or wrong data on other subsites.

Caching plugins are particularly problematic on multisite because each subsite needs its own cache bucket, and plugins like W3 Total Cache have had longstanding bugs with multisite file cache paths.

How to fix it

First rule: never network-activate a plugin unless its documentation explicitly states multisite support. Instead, activate per-site:

# Activate a plugin on a specific site only
wp plugin activate advanced-custom-fields --url=subsite.example.com

# Deactivate from network level if it's causing issues
wp plugin deactivate some-plugin --network

If a plugin has already created broken database tables across your network, you can clean them up:

# List custom tables for a specific site (site ID 3)
wp db query "SHOW TABLES LIKE 'wp_3_%';"

# Check for orphaned tables from a deleted subsite
wp db query "SHOW TABLES LIKE 'wp_5_%';"  # If site 5 no longer exists

For caching on multisite, WP Rocket and LiteSpeed Cache handle multisite properly. W3 Total Cache can work but requires careful configuration — set the cache path to use the blog ID and test each subsite individually.

WooCommerce on multisite deserves special mention: WooCommerce creates roughly 20 custom database tables per site. Network-activating WooCommerce on a 50-site multisite creates 1,000 tables. This is technically fine but can slow down database operations. If only a few subsites need WooCommerce, activate it per-site only.

10. WooCommerce + Stripe/Payment Gateway + Security Plugin

What happens

Customers reach the checkout page, enter their payment details, click “Place Order,” and get a generic error. The order is created in WooCommerce with a “Failed” status, but the customer’s card isn’t charged. Or sometimes the card IS charged but WooCommerce doesn’t register the payment — leading to a paid order stuck in “Pending payment” status. Webhooks from Stripe show as failing in the Stripe dashboard.

Why it happens

Payment gateway plugins communicate with external APIs (Stripe, PayPal, etc.) through webhooks and AJAX requests. Security plugins often block these communications in several ways:

The security plugin’s firewall blocks incoming webhook requests from Stripe’s servers because they don’t carry a WordPress nonce or logged-in user cookie. Stripe sends a POST request to yoursite.com/?wc-api=wc_stripe, and the security plugin sees an unauthenticated POST request and blocks it.

ModSecurity rules (either from the security plugin or server-level) flag the payment data as suspicious because it contains encoded JSON payloads that trigger SQL injection or XSS detection rules.

Rate limiting blocks Stripe’s webhook retries. If the first webhook attempt fails, Stripe retries with exponential backoff. The security plugin sees multiple requests from the same IP and blocks it as a brute-force attempt.

How to fix it

Whitelist payment gateway endpoints and webhook URLs in your security plugin. For Wordfence:

// Whitelist WooCommerce API endpoints in Wordfence
add_filter('wordfence_ls_require_captcha', function($required) {
    if (strpos($_SERVER['REQUEST_URI'], 'wc-api') !== false) {
        return false;
    }
    return $required;
});

Whitelist Stripe’s IP addresses in your firewall. Stripe publishes their webhook IPs — add them to your security plugin’s IP allowlist. You can find the current list at https://stripe.com/files/ips/ips_webhooks.json.

For server-level ModSecurity, add exclusion rules for WooCommerce endpoints:

# .htaccess — exclude WooCommerce API from ModSecurity
<IfModule mod_security2.c>
    SecRuleRemoveById 949110 959100
    <LocationMatch "/(wc-api|wp-json/wc)">
        SecRuleEngine Off
    </LocationMatch>
</IfModule>

Test webhooks after making changes. Stripe’s dashboard has a webhook testing tool — send a test event and verify it returns a 200 status:

# Check if the WooCommerce webhook endpoint is accessible
curl -s -o /dev/null -w "%{http_code}" -X POST https://yoursite.com/?wc-api=wc_stripe
# Should return 200, not 403 or 503

Check WooCommerce’s webhook delivery logs under WooCommerce > Status > Logs for any remaining errors.

How to Diagnose Any Plugin Conflict

When you’re not sure which plugins are fighting, here’s the systematic approach we use:

# 1. List all active plugins
wp plugin list --status=active --fields=name,version,update

# 2. Deactivate all plugins
wp plugin deactivate --all

# 3. Activate them one at a time, testing after each
wp plugin activate woocommerce
# Test the site
wp plugin activate wordfence
# Test again
# Continue until the conflict reappears

Enable debug logging to capture the exact error:

// wp-config.php
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);  // Don't show errors to visitors

Then check the log:

tail -100 wp-content/debug.log

The Query Monitor plugin is invaluable for identifying conflicts on a live site without disrupting visitors. It shows you which plugins are loading scripts, running database queries, and hooking into WordPress actions — all in the admin toolbar.

Prevention

Most plugin conflicts are preventable with a few habits:

Stage everything. Never update plugins on your live site. Use a staging environment, update there, test, then push to production. If your host doesn’t offer staging, set one up with WP-CLI:

# Export production database
wp db export production.sql

# Import to staging
wp db import production.sql --url=staging.yoursite.com
wp search-replace 'yoursite.com' 'staging.yoursite.com'

Fewer plugins, better plugins. Twenty well-maintained plugins cause fewer issues than forty mediocre ones. Before installing any plugin, check: when was it last updated? Does it have unresolved support threads about conflicts? Is it tested with your WordPress version?

Monitor your error logs. Plugin conflicts often show up in debug.log as PHP warnings before they become visible errors. Set up log monitoring and catch issues before your visitors do.

Keep one plugin per function. One SEO plugin. One caching plugin. One security plugin. One image optimizer. Overlap is where conflicts live.

We Fix Plugin Conflicts Every Day

If you’re dealing with a plugin conflict that you can’t sort out — or if your site broke after an update and you need it fixed fast — that’s what our website troubleshooting service is built for. We diagnose the root cause, fix the conflict, and set up monitoring so it doesn’t happen again.

Get in touch and tell us what’s going on. We usually respond within a few hours on business days.

Need help with your WordPress site?

We can help with the stuff covered in this post. Message us and we'll figure it out.