Category: Hosting

  • WordPress transient bloat: when temporary data becomes permanent

    WordPress transient bloat: when temporary data becomes permanent

    WordPress transient bloat: when temporary data becomes permanent
    Photo: Matinbeigi via Wikimedia Commons (CC BY-SA 4.0)

    WordPress transients are supposed to be temporary. They’re API response caches, widget output buffers, and rate-limit trackers that plugins and themes store in your database with an expiration timestamp. The system promises automatic cleanup.

    It doesn’t happen. Most WordPress installs accumulate thousands of expired transients that sit in the wp_options table indefinitely. The result: slower admin queries, bloated database exports, and—if you’re unlucky—autoload overhead that adds 200–500ms to every single page load.

    What transients are and why they pile up

    Transients live in wp_options as key-value pairs. Each transient gets two rows: one for the data (_transient_name) and one for the expiration timestamp (_timeout_transient_name).

    WordPress checks the timeout when you request a transient. If it’s expired, the system returns false and is supposed to delete both rows. That deletion happens sometimes—but only when code explicitly requests the expired transient by name.

    If a plugin stops using a transient key, or if a theme gets deactivated, or if an API endpoint changes and the cache key shifts, those rows stay forever. Multiply that across a dozen plugins over two years, and you’re carrying 8,000–15,000 expired transients.

    I audited a client site last month running WooCommerce, Yoast, and a page builder. The wp_options table held 11,400 transient rows. Fewer than 200 were still valid. The rest dated back to 2024.

    When bloat becomes a performance problem

    Not all transient bloat is slow. If the transients aren’t autoloaded, they sit inert until a query scans the full table—during a plugin settings save, a database export, or a migration.

    The danger zone is autoloaded transients. WordPress loads every autoloaded option into memory on every page load, whether you use it or not. Plugins that set transients with autoload enabled (some do this by accident) can inject 50–100 KB of dead data into every request.

    Run this query to check autoloaded size:

    SELECT SUM(LENGTH(option_value)) FROM wp_options WHERE autoload = 'yes';

    If the result exceeds 800 KB, you’ve got a problem. Above 1.5 MB, every page load takes a measurable hit. Autoloaded transients are often the culprit, especially from caching plugins, social-share counters, and API widgets.

    How to purge expired transients safely

    Purging is a SQL operation. Before you run it, back up your database. Then use this query to delete expired transient pairs:

    DELETE FROM wp_options WHERE option_name LIKE '_transient_%' AND option_name NOT LIKE '_transient_timeout_%' AND (SELECT option_value FROM wp_options WHERE option_name = CONCAT('_transient_timeout_', SUBSTRING(option_name, 12))) < UNIX_TIMESTAMP();

    Most managed WordPress hosts—including BigScoots—let you run SQL directly from phpMyAdmin or via WP-CLI. If you’re on WP-CLI, install the Transient Command package:

    wp package install wp-cli/transient-command
    wp transient delete --expired

    This is safer and faster than a manual query. It handles both the data row and the timeout row in one pass.

    After purging, check autoload size again. On the client site I mentioned, autoloaded options dropped from 1.8 MB to 420 KB. Admin page load times improved by 340ms. Front-end caching masked most of the visitor-facing impact, but logged-in users saw faster dashboard responses immediately.

    Prevention: audit transient usage in your plugin stack

    The best fix is limiting transient creation in the first place. Audit which plugins are setting transients and whether they clean up after themselves.

    Install the Query Monitor plugin and visit a few pages on your site. Open the “Database Queries” panel and filter for set_transient calls. Note which plugins fire the most transient writes. Then check whether those plugins offer settings to disable or reduce cache frequency.

    Social share counters and related-post plugins are the worst offenders. Many set transients for every post or every API response, with 24-hour expirations that renew on every view. If you have 500 posts and a social-share plugin that caches Twitter and Facebook counts separately, that’s 1,000+ transients rotating daily.

    Disable aggressive transient caching where you don’t need real-time data. Most operators don’t need share counts to refresh more than once a week.

    If you run WooCommerce, audit your transient load after enabling or updating extensions. WooCommerce core is disciplined about transient cleanup, but third-party extensions frequently aren’t.

    When to schedule purges

    If you can’t eliminate the source, automate the cleanup. Add a monthly WP-CLI cron job or use a plugin like WP-Optimize (free version includes transient cleanup). Schedule it to run during low-traffic hours.

    Don’t purge daily—it’s unnecessary and adds load. Monthly is enough unless you’re running a high-transaction WooCommerce store or a membership site with thousands of users generating session transients. In those cases, weekly makes sense.

    One non-obvious tip: after a major plugin update or deactivation, manually purge transients. Plugin uninstallers rarely clean up their own transient keys. Deactivating a page builder or switching analytics plugins can leave hundreds of orphaned cache entries behind.

    Want more hosting and WordPress infrastructure breakdowns? Subscribe to One Two Three Send for weekly operator-focused guides on the tooling that powers online businesses.

  • WordPress staging sync: which direction breaks production data

    Most WordPress hosting dashboards offer one-click staging environments. You spin up a copy of your site, test a plugin update or theme change, then push it live. Simple enough—until you sync the wrong direction and overwrite three weeks of production subscriber data.

    The confusion isn’t whether to use staging. It’s understanding what should move from staging to production, what should move the other way, and what should never sync at all.

    What belongs in a staging-to-production push

    Code and configuration changes are safe to push from staging to live. That includes:

    • Plugin updates and new plugin installs
    • Theme files and customizer settings
    • Permalink structure changes
    • New page templates or custom post types

    These are deterministic changes. If a plugin works in staging, it works in production. The risk is compatibility, not data loss—and you’ve already tested that in staging.

    Most managed WordPress hosts—BigScoots, WP Engine, Kinsta—offer a “push to live” button that moves files and database tables selectively. The default behavior pushes the entire database, which is where things break.

    What should pull from production to staging

    User-generated data moves the opposite direction. If your site accepts comments, form submissions, new user registrations, or ecommerce orders, that data lives in production first. Staging should pull fresh copies before you test anything that touches those tables.

    The most common mistake: testing a membership plugin update in staging, then pushing the entire database to production. You’ve just overwritten every new subscriber who joined in the last week.

    A safer workflow pulls production data into staging before you start testing. Most hosts let you clone the live database to staging on demand. Do that first. Test your changes against current data. Then push only the code and configuration changes back to production—not the database.

    What should never sync in either direction

    Some data exists only in one environment and should stay there:

    • API keys and credentials. Staging should use test-mode keys for payment processors, email services, and third-party APIs. If you push staging’s wp-config.php to production, you’ll send live transactions through a sandbox.
    • Email sending settings. Transactional email plugins like WP Mail SMTP should point to a dev inbox in staging, not your live Postmark account. Otherwise, every test form submission emails real users.
    • Cache and transient data. Staging and production maintain separate object caches. Syncing wp_options tables can copy stale transients that break live caching.
    • User sessions. Pushing staging sessions to production logs everyone out. Pulling production sessions to staging lets you access live user accounts from a test environment—a security risk if staging is less hardened.

    Most hosts exclude these tables by default, but custom setups don’t. If you manage your own staging sync via WP-CLI or a plugin like WP Migrate DB, you’re responsible for the exclusion list.

    How to build a safe sync checklist

    Before you push anything from staging to production, answer three questions:

    Did production data change since I cloned staging? If yes, pull fresh data into staging first, re-test, then push only code.

    Am I pushing database tables? If yes, which ones? Core WordPress tables like wp_posts and wp_options are usually safe if no content changed. But wp_users, wp_comments, and any custom tables tied to live user activity should stay in production.

    Are environment-specific settings isolated? Check wp-config.php, .env files, and any plugin settings that store API keys. Staging and production should never share credentials.

    A conservative rule: push files, pull data. Treat the database as append-only in production. If you need to migrate schema changes—new custom fields, altered table structures—push those separately using a migration plugin that applies changes without overwriting rows.

    The hosting dashboard’s one-click sync is convenient until it isn’t. Know what it’s actually moving, and when to do it manually instead.

    Got a staging horror story or a sync workflow that works? Reply and let me know—I’m collecting operator war stories for a future piece.

  • WordPress CDN services: when they slow your site instead of speeding it up

    WordPress CDN services: when they slow your site instead of speeding it up

    Content delivery networks sit near the top of every WordPress performance checklist. The pitch is simple: distribute static assets across edge servers, cut latency, serve files faster. For many solo operators, though, a CDN introduces more overhead than it solves—especially when you’re running a text-first site with minimal traffic.

    The problem isn’t CDNs themselves. It’s the gap between what they’re built for and how most small operators deploy them.

    When CDN cache misses cost more than direct origin requests

    A CDN works by caching copies of your assets at edge locations close to visitors. When someone requests a file, the CDN checks its cache. If the file exists (a cache hit), the CDN serves it directly. If it doesn’t (a cache miss), the CDN pulls the file from your origin server, caches it, then serves it to the visitor.

    That origin pull adds latency. For the first visitor after a cache expires, the request travels to the CDN, then to your origin, then back to the CDN, then to the visitor. That’s slower than a direct origin request—sometimes by 200–400 milliseconds.

    If your site gets 500 visitors a day and your CDN cache expires every four hours, a meaningful percentage of requests trigger origin pulls. The benefit of edge caching evaporates when cache-hit ratios stay below 70%. Sites with low traffic or frequently changing content spend more time in cache-miss penalty than they gain from edge delivery.

    Check your CDN dashboard’s cache-hit ratio. If it’s under 75%, you’re paying for infrastructure that slows more requests than it accelerates.

    HTML caching at the edge breaks dynamic WordPress features

    Most CDN providers let you cache HTML, not just static assets like images and CSS. The promise: serve entire pages from the edge without touching your origin server at all.

    The catch: WordPress is dynamic. Comment counts update. Logged-in users see personalized navigation. Cart contents change. Caching full HTML breaks all of that unless you configure cache exceptions for every dynamic element.

    Cloudflare’s default WordPress rules exclude logged-in users and query strings, but many operators enable aggressive HTML caching to chase Lighthouse scores. The result: broken checkout flows, stale post counts, and support requests from confused readers.

    If you cache HTML, you need rules that exclude:

    • Any page with cookies (wp-admin, cart, user sessions)
    • URLs with query strings (?utm_source, ?add-to-cart)
    • POST requests (forms, comments)
    • Paths where content updates frequently (/feed, /comments)

    Most small operators don’t have the traffic volume to justify the complexity. Caching assets only—images, CSS, JavaScript—delivers 80% of the performance gain with none of the breakage.

    CDN costs scale faster than traffic for small sites

    Free CDN tiers sound appealing, but bandwidth limits hit sooner than expected. Cloudflare’s free plan is genuinely unlimited for most use cases, but competitors like BunnyCDN, StackPath, and KeyCDN charge per gigabyte after you cross 100–500 GB per month.

    A single viral post with embedded images can push a small site past free-tier limits. BunnyCDN charges $0.01–0.04 per GB depending on region. If a post with 2 MB of images gets 50,000 pageviews in a weekend, that’s 100 GB of bandwidth—$1 to $4 in CDN costs. Not catastrophic, but surprising if you budgeted for free infrastructure.

    For most solo operators running text-heavy WordPress sites with under 10,000 monthly visitors, the marginal speed gain from a CDN doesn’t justify the configuration overhead or the risk of surprise bandwidth bills. A well-tuned origin server with image compression and browser caching delivers load times under two seconds—fast enough for retention and SEO.

    When a CDN actually helps

    CDNs make sense when:

    You serve large files. Video embeds, downloadable PDFs, high-resolution images, or course assets benefit from edge delivery. A 50 MB video file served from a Singapore edge node loads faster for an Australian visitor than pulling from a US origin server.

    Your traffic is geographically distributed. If your analytics show 30% of visitors from Europe, 30% from Asia, and 40% from North America, a CDN cuts cross-continent latency. If 90% of your traffic comes from one region, a well-located origin server is simpler.

    You exceed 50,000 monthly pageviews. At that scale, cache-hit ratios stabilize above 80%, and the performance gain outweighs configuration complexity.

    Before you add a CDN, run a baseline test. Use WebPageTest or GTmetrix to measure time to first byte (TTFB) and fully loaded time from multiple locations. Then enable the CDN, configure asset-only caching, and re-test. If TTFB improves by less than 150 milliseconds, the CDN isn’t doing much.

    Want more operator-to-operator breakdowns like this? Subscribe to One Two Three Send for weekly deep-dives on the tools and infrastructure decisions that matter for small online businesses. No fluff, just the details that save you time and money.

  • WordPress managed hosting SSH access: what you lose and why

    WordPress managed hosting SSH access: what you lose and why

    When you move from a VPS or shared cPanel host to managed WordPress hosting, one of the first things you might notice is limited—or completely absent—SSH access. Some hosts disable it by default. Others gate it behind higher-tier plans. A few remove it altogether and route everything through proprietary dashboards or Git workflows.

    If you’ve spent years running wp-cli commands, deploying via rsync, or tailing error logs in real time, the loss stings. But managed hosts aren’t just being difficult. The trade-off is deliberate, and understanding it helps you decide whether the convenience is worth what you’re giving up.

    What actually disappears when SSH goes away

    The most immediate loss is direct command-line access to your WordPress install. That means:

    • No WP-CLI. Bulk operations—importing posts, regenerating thumbnails, flushing rewrite rules—now require plugins or support tickets.
    • No custom deploy scripts. If your workflow relies on rsync, scp, or Git hooks that push files directly to the server, you’ll need to adapt to the host’s deployment tools or SFTP.
    • No real-time log access. Tailing error.log or access.log via SSH is out. Most managed hosts surface logs through their dashboard, but with a delay—sometimes five to fifteen minutes.
    • No cron job customisation. If you’ve been scheduling tasks via crontab -e, you’re now limited to WordPress’s built-in wp-cron or the host's scheduled task interface.

    For solo operators who've built muscle memory around SSH workflows, this feels like a downgrade. And in some cases, it is.

    What you gain in exchange

    Managed WordPress hosts strip SSH access because it reduces the surface area for things to break. When they control how you interact with the server, they can:

    • Enforce object caching and page-cache rules. Hosts like Kinsta and WP Engine bake Redis or Memcached into the stack and manage purge behaviour automatically. If you could SSH in and flush caches manually, you'd bypass their optimisations—and their support team would struggle to diagnose performance issues.
    • Prevent bad plugin installs. Some hosts scan uploads and block known-bad plugins or themes before they hit the filesystem. SSH access would let you sidestep that.
    • Standardise environments. When everyone uses the same deployment method—whether it's SFTP, a Git push, or a dashboard upload—the host can guarantee file permissions, directory structure, and PHP handler consistency.

    The result: faster support, fewer "it works on my machine" tickets, and a tighter performance baseline. For operators who don't want to think about server tuning, that's a win.

    When the restriction actually hurts

    SSH restrictions cause real problems in three scenarios:

    Migration and bulk imports. If you're moving a large site—10,000+ posts, extensive custom taxonomies, or complex metadata—WP-CLI's import commands are orders of magnitude faster than browser-based plugins. Without SSH, you're stuck waiting for PHP timeouts or splitting files into tiny batches.

    Custom integrations. If your business relies on a custom sync script—pulling data from an external API and writing it directly to wp_postmeta, for example—you'll need to rewrite it as a plugin or cron job that runs inside WordPress's PHP environment. That's slower and harder to debug.

    Granular troubleshooting. When a plugin conflict or theme bug crashes your site, SSH access lets you rename directories, disable plugins via the filesystem, and check error logs in real time. Without it, you're waiting for support to do it for you—or fumbling with SFTP and hoping you don't make things worse.

    How to adapt (or avoid the problem)

    If you're on a managed host without SSH and you need command-line access, a few hosts offer middle-ground options:

    • BigScoots offers SSH on all managed WordPress plans, with WP-CLI pre-installed. You get the performance stack and support model of managed hosting without losing terminal access.
    • Cloudways and GridPane both provide full SSH and let you configure cron jobs, deploy via Git, and run custom scripts.
    • Kinsta and WP Engine don't offer SSH, but both have robust staging environments and Git-based deployment workflows. If you can shift your workflow to Git push instead of rsync, you'll regain most of the flexibility.

    If you're evaluating hosts and SSH is non-negotiable, ask explicitly during onboarding. Some sales reps will say "limited SSH" when they mean "SFTP only." Get specifics: can you run WP-CLI? Can you tail logs? Can you schedule cron jobs outside WordPress?

    The real question

    SSH access isn't inherently good or bad. It's a tool. If your workflow depends on it—bulk imports, custom scripts, real-time debugging—choose a host that supports it or be ready to rewrite your processes. If you'd rather offload server management entirely and trust the host's optimisations, the loss won't hurt.

    The mistake is assuming all managed WordPress hosting is the same. It's not. Some hosts remove SSH to simplify support. Others preserve it because their customers need it. Know which camp you're in before you sign up.

    What's your experience? Hit reply and let us know whether SSH restrictions have been a dealbreaker or a relief. We read every response.

  • WordPress plugin auto-updates: when to enable and when to wait

    WordPress plugin auto-updates: when to enable and when to wait

    WordPress added automatic plugin updates in version 5.5, and the feature has quietly become one of the most misunderstood levers in the admin dashboard. Enable it everywhere and you risk pushing a breaking change to production at 3 a.m. Disable it completely and you’re one unpatched vulnerability away from a compromised site.

    The decision isn’t binary. Most operators treat auto-updates as an all-or-nothing switch, but the smarter play is selective: some plugins earn the privilege, others don’t.

    What actually happens when you enable auto-updates

    When you toggle auto-updates for a plugin, WordPress adds it to a background job that runs twice daily. If a new version is available and passes a few basic checks—compatible WordPress version, no obvious conflicts—the update executes without asking.

    The update happens during a cron event, not in real time. If your site has low traffic or your server’s cron is misconfigured, the update may delay by hours. That’s not a bug; it’s how WordPress schedules background tasks.

    WordPress sends an email after each auto-update. If you’re not seeing those, check your transactional email setup—most hosts route wp_mail() through unreliable SMTP by default. If you’re using Postmark or another dedicated transactional service, the notifications land reliably.

    One detail most guides skip: auto-updates do not create database backups before running. If a plugin update breaks your schema or corrupts data, you’re restoring from your last manual or scheduled backup. That’s why staging environments matter.

    Which plugins to trust with auto-updates

    Not all plugins carry the same risk profile. Small, single-purpose utilities—contact forms, social share buttons, analytics connectors—rarely introduce breaking changes. They touch a narrow slice of your site and update infrequently.

    Security-focused plugins are the best candidates: Wordfence, Sucuri, iThemes Security. These update often, and delaying a security patch is riskier than the update itself. The vendors test obsessively because their reputation depends on it.

    Plugins maintained by large platforms—Jetpack, WooCommerce, Yoast SEO—also qualify. They have QA teams, beta cycles, and rollback mechanisms. Auto-updating these is less risky than manually updating at random intervals.

    Avoid auto-updates for plugins that:

    • Modify your database schema (membership plugins, custom post-type builders)
    • Interact with payment processors (WooCommerce extensions, Easy Digital Downloads add-ons)
    • Control caching or performance (WP Rocket, LiteSpeed Cache—these can break rendering)
    • Hook deeply into your theme (page builders, custom fields)

    For these, the cost of an unexpected break in production outweighs the convenience of hands-off updates.

    How to test updates before enabling auto-update

    If you’re running a staging environment, clone production, enable auto-updates there first, and wait two weeks. If nothing breaks, enable it on the live site. Staging environments catch 80% of update conflicts before they hit real traffic.

    If you don’t have staging, manual-update high-risk plugins during low-traffic windows. Check your analytics for the quietest two-hour block each week—usually early morning in your primary timezone—and batch updates then. Keep your browser open for ten minutes after updating and test key workflows: checkout, form submissions, member login.

    For plugins that update frequently—weekly or more—auto-updates make sense even if they’re high-risk, because the manual burden becomes unsustainable. You’ll spend more time clicking “Update” than you’ll lose to occasional rollbacks.

    The non-obvious tip: monitor update frequency before committing

    Before enabling auto-updates, check the plugin’s update history on WordPress.org. Click “Development” in the plugin’s listing and scan the changelog. If the vendor ships five updates a month with vague notes like “bug fixes” or “performance improvements,” that’s a red flag. Frequent, low-detail updates suggest poor testing or reactive development.

    Plugins that update every 60–90 days with detailed changelogs are safer bets. The vendor is batching changes, running QA, and treating releases as events rather than continuous patches.

    One more filter: check the “Active Installations” count. Plugins with 100,000+ installs get battle-tested by a large user base. If a breaking change ships, it’s caught and patched within hours. Plugins with fewer than 10,000 installs don’t have that safety net.

    Auto-updates work best when the plugin vendor has more to lose than you do. High-profile plugins with millions of users can’t afford to break sites at scale. Niche plugins with small audiences can.

    Want more deployment and workflow tips? Subscribe to One Two Three Send—every issue covers one specific decision point for operators running content businesses.

  • WordPress Staging Environments: What Actually Needs Testing

    WordPress Staging Environments: What Actually Needs Testing

    You spin up a staging site, clone production, test a plugin update, see it works, push live. Two days later something breaks. The staging test missed it.

    Staging environments promise safety. They deliver it only when you know what actually needs testing—and what’s theater.

    Most solo operators and small teams treat staging as a checkbox: “I tested it.” But staging sites differ from production in ways that make certain tests useless and certain risks invisible. Here’s what to focus on when you’ve got limited time and a site that can’t afford downtime.

    Test Database-Dependent Changes, Skip Static Ones

    Plugin updates that touch the database—anything involving custom post types, user roles, or schema changes—need staging. A membership plugin, an SEO tool that rewrites permalinks, a form builder that stores submissions: these can corrupt data or break queries if they fail mid-migration.

    Static changes—new CSS, a JavaScript library update, a theme tweak that doesn’t touch PHP logic—can often be tested locally or on a low-traffic page. Staging won’t tell you much more than your browser’s dev tools already do.

    The dividing line: if the change writes to wp_options, wp_posts, wp_users, or custom tables, stage it. If it’s front-end only and reversible with FTP, you can skip the full staging ritual.

    Replicate Traffic Patterns, Not Just Data

    A cloned database and codebase won’t show you how a change performs under load. Staging sites run on lighter server specs, handle zero concurrent users, and skip most caching layers. That makes them poor predictors of performance.

    If you’re testing a resource-heavy plugin—an analytics injector, a lazy-load script, a page builder—check server resource usage in staging, then simulate traffic with a tool like Query Monitor or a simple load test script. Watch for memory spikes, slow database queries, and HTTP request waterfalls.

    Don’t bother testing page-load speed in isolation. Staging won’t have your CDN, your object cache, or your production traffic patterns. The metric that matters is relative performance: does the change make queries slower or add significant overhead?

    Test Integration Points, Not Just Features

    The plugin works in staging. But does it work with your email delivery, your payment processor, your analytics, your CDN purge rules?

    Most staging environments disable or sandbox third-party API calls. That’s smart for safety, but it means your tests skip the failure modes that break production: a conflict between your caching plugin and your transactional email setup, a payment webhook that doesn’t fire after a checkout-flow redesign, a CDN that stops purging after you change permalink structure.

    Before you push live, test one full user flow that touches external services. Sign up, receive an email, complete a purchase, check that analytics fire. Use staging API keys where possible. If your staging site can’t connect to real services, document which integration points weren’t tested—and monitor them closely post-deploy.

    When Staging Wastes Time

    Staging is overkill for editorial changes, minor copy edits, and media uploads. It’s also overkill for plugin updates from trusted sources that don’t touch your database schema—security patches, minor version bumps with no migration scripts.

    If your staging workflow takes more than ten minutes to clone, test, and sync back, you’ll start skipping it. That’s worse than a streamlined process that tests only high-risk changes.

    For low-stakes updates, use a checklist instead: back up the database, update, check two representative pages, verify one user flow. If it breaks, restore. If your host offers one-click staging, use it. If you’re manually cloning databases and running search-replace scripts, reserve staging for changes that justify the time cost.

    What to Do After You Push

    Staging can’t catch everything. Production has real traffic, real user behavior, real edge cases. After you deploy, monitor actively for 24 hours.

    Check server error logs, watch for broken form submissions, scan your checkout flow if you touched payments, verify email delivery if you updated SMTP routing. Use a simple uptime monitor—Uptime Robot’s free tier works—and set an alert for HTTP 500 errors.

    If something breaks, your staging tests didn’t predict it. Add that failure mode to your next staging checklist. Over time, you’ll build a list of tests that actually matter for your site, not just the generic advice.

    What’s your staging workflow? Reply and tell us what you test before you push live—or what broke in production despite passing staging. We read every reply.

  • WordPress transactional email plugins route through the wrong SMTP server

    WordPress transactional email plugins route through the wrong SMTP server

    If you’re running a content business on WordPress—memberships, course sales, paywalled newsletters—you’ve probably installed WP Mail SMTP, Easy WP SMTP, or Post SMTP to fix the “WordPress emails not sending” problem. These plugins work. But most solo operators configure them once and never check which emails are actually routing through which server.

    The result: transactional emails like password resets, purchase confirmations, and login codes get sent through the same SMTP connection as your marketing broadcasts. That’s a deliverability risk, a compliance headache, and in some cases a direct violation of your ESP’s terms of service.

    Why WordPress doesn’t care which emails are transactional

    WordPress core uses a single function—wp_mail()—to send every outbound email. Plugin update notifications, comment moderation alerts, WooCommerce order confirmations, and member login links all call the same function. There’s no built-in distinction between transactional and promotional.

    When you install WP Mail SMTP and point it at your marketing ESP—say, MailerLite or Brevo—you’ve just told WordPress to route everything through that SMTP endpoint. MailerLite’s terms explicitly prohibit sending transactional email through their marketing SMTP relay. Brevo allows it, but only if you’ve configured a dedicated transactional sender and separated your IP reputation.

    Most operators skip that step. They plug in their SMTP credentials, see the test email arrive, and move on. Six months later, a member complains they’re not getting password reset emails. You check spam folders, then logs, then realize your marketing ESP flagged the reset email as suspicious because it came from a domain with no SPF record for transactional sending.

    What actually breaks

    The failure modes vary by ESP and plugin, but the common ones are:

    • Rate limits. Marketing ESPs throttle SMTP connections differently than transactional providers. MailerLite’s SMTP relay allows 200 emails per hour on the free tier. If your site sends 50 comment notifications in an hour, you’ve used a quarter of your sending quota before your next broadcast even starts.
    • IP reputation bleed. When you send password resets through the same IP pool as your newsletter, a spam complaint on one affects the other. Transactional emails have higher open rates and lower complaint rates—mixing them with promotional content drags both down.
    • Logging and compliance gaps. Marketing platforms log email for segmentation and engagement tracking. Transactional platforms log for delivery confirmation and audit trails. Sending a purchase receipt through a marketing ESP means that email gets added to a subscriber’s engagement history, skewing your open-rate metrics and potentially violating GDPR’s purpose-limitation principle.

    Postmark, which specializes in transactional email, charges $10 per 10,000 emails with no monthly fee. Their SMTP relay is configured specifically for password resets, receipts, and system notifications—high deliverability, minimal tracking, and logs built for compliance. If you’re sending fewer than 10,000 transactional emails a month, the cost is a rounding error compared to the risk of your members not receiving login codes.

    How to split your sending in WP Mail SMTP

    WP Mail SMTP Pro (the paid tier, starts at $49/year) includes a feature called “Email Log” and “Email Controls” that let you route specific email types through different SMTP configurations. The free version doesn’t support multiple SMTP accounts, so you’ll need either the Pro version or a separate plugin like Post SMTP (free, supports multiple mailers).

    Here’s the setup for Post SMTP:

    • Install Post SMTP from the WordPress plugin repository.
    • Add your transactional SMTP credentials first—Postmark, Amazon SES, or Brevo’s transactional API. Set this as the default mailer.
    • Under “Message” settings, enable “Additional SMTP Settings” and add your marketing SMTP credentials as a secondary mailer.
    • In the “Email Log” settings, create a rule: if the email subject contains “password” or “reset” or “order,” route through the transactional mailer. Everything else goes through marketing.

    Post SMTP’s rules engine uses regex, so you can get granular. If you’re running WooCommerce, you can route all emails where the sender address matches [email protected] through transactional, and leave everything from [email protected] on the marketing relay.

    One overlooked detail: SPF and DKIM per sender

    Even if you route emails correctly, your DNS records need to authorize both SMTP servers. If your transactional emails come from [email protected] and your newsletters come from [email protected], both domains need SPF records that include both Postmark’s and MailerLite’s sending IPs.

    Most operators add one SPF record and assume it covers everything. It doesn’t. Each sending domain needs its own record, and if you’re using subdomains (mail.yourdomain.com vs. yourdomain.com), each subdomain needs separate DNS entries. Postmark’s onboarding checklist walks you through this. MailerLite’s does not—you have to dig into their documentation.

    The fastest way to verify: send a test email from each SMTP connection, then check the raw email headers. Look for spf=pass and dkim=pass in the Authentication-Results field. If either shows softfail or none, your DNS isn’t configured correctly.

    Want more tooling breakdowns like this? Subscribe to One Two Three Send—one article every morning, no fluff, no affiliate pressure.

    Heads up — some links in this article are affiliate links. If you sign up through them, we may earn a small commission at no extra cost to you. We only recommend tools we use ourselves.

  • WordPress performance plugins double your server load—here’s why

    WordPress performance plugins double your server load—here’s why

    Installing a WordPress performance plugin feels like the responsible thing to do. Your PageSpeed score is stuck in the 60s, your hosting dashboard shows CPU spikes, and every SEO guide tells you to optimize. So you install WP Rocket, W3 Total Cache, or Autoptimize, flip a few switches, and assume the problem is solved.

    Then your server load doubles.

    This isn’t a edge case. Performance plugins introduce their own overhead—database writes for cache keys, background processes to minify assets, recurring tasks to preload pages—and many operators never realize the plugin meant to speed things up is now the bottleneck.

    What performance plugins actually do

    Most WordPress performance plugins tackle three jobs: page caching, asset minification, and delivery optimization (lazy loading, CDN integration, font preloading). Each of these requires the plugin to intercept requests, rewrite HTML on the fly, or generate new files.

    Page caching works by saving a static HTML version of each page to disk or object cache, then serving that cached copy instead of rebuilding the page from the database every time. Asset minification combines and compresses CSS and JavaScript files. Delivery optimization defers or delays non-critical resources.

    The problem: all of these tasks add new processes. A caching plugin has to check whether a cached version exists, determine if it’s stale, regenerate it if needed, and manage cache invalidation when you publish new content. Minification plugins parse your CSS and JS on every change, write new files to disk, and track dependencies. Lazy-loading scripts inject JavaScript that monitors scroll position and loads images dynamically.

    If your site was already near its resource limit, these new tasks push you over. You’re now running two systems: WordPress and the performance layer, each competing for CPU and memory.

    Where the overhead hides

    The most common culprit is automatic cache preloading. Many caching plugins offer a “preload” option that crawls your entire site in the background, generating cached versions of every page. Sounds efficient—until you realize it’s firing dozens or hundreds of requests against your own server, often during peak traffic hours.

    On a site with 200 pages, preloading can mean 200 simultaneous PHP processes trying to render pages. If your host limits you to 20 concurrent PHP workers, you’ve just locked out real visitors while the plugin talks to itself.

    Database writes are another hidden cost. Some caching plugins write cache metadata to the WordPress database: expiration timestamps, cache keys, file paths. If you’re generating thousands of cached variations (different pages, mobile vs. desktop, logged-in vs. logged-out), you’re writing thousands of database rows. Those writes slow down every other query on your site.

    Asset minification can backfire if it runs on every page load instead of only when files change. Plugins that regenerate minified CSS and JS on the fly—rather than saving the output and reusing it—burn CPU on repetitive work. If your theme or page builder outputs inline styles, the plugin may re-minify the same code hundreds of times per day.

    How to audit the damage

    Start with your hosting dashboard. Most managed WordPress hosts (BigScoots, Kinsta, WP Engine) show CPU and memory graphs. Look for spikes that align with plugin activation or cache preload schedules.

    Next, check database query counts. Install the Query Monitor plugin, load a few pages, and compare query volume before and after activating your performance plugin. If queries increase by 20 or more, the plugin is reading or writing cache metadata on every request.

    Check background processes with a cron monitor. WP Crontrol is a free plugin that lists all scheduled tasks. Look for jobs labeled “cache preload,” “minify regenerate,” or “optimize images.” If they run every hour and your site has 500+ pages, you’re hammering your server for minimal gain.

    Finally, test actual page speed with and without the plugin. Use WebPageTest or GTmetrix to measure Time to First Byte (TTFB) and Largest Contentful Paint (LCP) with the plugin active, then deactivate it and test again. If TTFB increases with the plugin enabled, it’s doing more harm than good.

    What to do instead

    Start by offloading work your server shouldn’t be doing. Use a CDN (Cloudflare, BunnyCDN) to serve static assets—images, CSS, JS—without touching your origin server. Enable Cloudflare’s automatic minification and Brotli compression so you’re not running those tasks in PHP.

    If you need page caching, choose a plugin that writes to disk (not the database) and doesn’t preload unless you explicitly trigger it. WP Super Cache and Cache Enabler both write flat HTML files and stay out of the database. Disable mobile-specific caching unless you’re serving completely different markup to mobile users—responsive design means one cached version works for everyone.

    Skip asset minification unless you’re loading 15+ CSS or JS files per page. Modern HTTP/2 hosting handles multiple small files efficiently, and the CPU cost of minification often outweighs the bandwidth savings. If you do minify, use a build tool (Webpack, Vite) during development so the minified files are static—never generated at runtime.

    For image optimization, use a service like ShortPixel or Imagify that processes images once and stores the result, rather than a plugin that optimizes on every request. Set it to manual mode and run it after uploading new images, not automatically.

    Most importantly: only add a performance plugin if you’ve measured the problem first. If your TTFB is already under 600ms and LCP is under 2 seconds, you don’t need caching. If your total page weight is under 1MB, you don’t need aggressive minification. Add complexity only when the data proves you need it.

    Reply with the hosting setup or performance plugin you’re running—I’ll feature operator setups and server configs in a future issue.

  • WordPress object caching: what it fixes and when to skip it

    WordPress object caching: what it fixes and when to skip it

    Object caching is one of those WordPress optimizations everyone talks about but few operators actually understand. The idea is simple: instead of hitting the database every time someone loads a page, WordPress stores frequently accessed data—posts, user info, theme settings—in memory. When the same data is requested again, it’s served from RAM instead of MySQL.

    That sounds like a win. And for many sites, it is. But object caching introduces moving parts—another service to monitor, another thing that can fail—and if your traffic or query load doesn’t justify it, you’re adding complexity for marginal gain.

    What object caching actually does

    Out of the box, WordPress uses a non-persistent object cache. That means data is stored in PHP memory for the duration of a single page load, then discarded. If the next visitor requests the same page, WordPress queries the database all over again.

    A persistent object cache—typically Redis or Memcached—stores that data in memory across page loads. When WordPress asks for a post’s metadata or a list of recent comments, the cache serves it instantly. No database round trip.

    This makes the biggest difference on sites with:

    • High concurrent traffic (50+ visitors at once)
    • Complex database queries (custom post types, meta fields, WooCommerce)
    • Plugins that query the database on every page load (membership tools, dynamic content widgets)

    If your site doesn’t fit those profiles, object caching won’t do much. A static homepage with 20 posts and light traffic won’t see a speed boost—you’re already fast enough.

    When to skip it

    Object caching adds a dependency. If Redis goes down, your site slows to a crawl—or breaks entirely, depending on how your host configured the fallback. Managed WordPress hosts like BigScoots typically handle this gracefully, but if you’re self-managing a VPS, you need monitoring in place.

    You also need to flush the cache deliberately when you update content. Most caching plugins handle this automatically, but edge cases exist: custom fields updated via WP-CLI, direct database edits, or theme changes can leave stale data in Redis for hours.

    Skip object caching if:

    • Your traffic is under 10,000 page views per month
    • Your average page load is already under 1 second (check your server response time in Google Search Console or Pingdom)
    • You’re running a static site generator or headless setup (the database isn’t the bottleneck)

    In those scenarios, you’ll get more value from a CDN, image optimization, or trimming down plugins. Object caching is overkill.

    How to set it up (if you need it)

    Most managed WordPress hosts offer Redis or Memcached as a one-click add-on. If you’re on a VPS, you’ll need to install Redis via SSH, then add a PHP extension and a WordPress plugin to connect the two.

    The most common plugin is Redis Object Cache by Till Krüss. It’s free, actively maintained, and shows you cache hits versus misses in the WordPress admin. If your hit rate is below 80%, something’s misconfigured—either the cache isn’t being populated correctly, or your queries aren’t cacheable.

    After enabling object caching, test these scenarios:

    • Load your homepage twice in quick succession. The second load should be faster.
    • Publish a new post and confirm it appears immediately (not after a manual cache flush).
    • Check your server’s memory usage. Redis should sit around 50–100 MB for a typical site. If it’s ballooning past 500 MB, your cache isn’t expiring old data.

    One non-obvious tip

    Not all database queries should be cached. User-specific data—cart contents, login states, personalized recommendations—needs to stay dynamic. If you cache those, you’ll serve the wrong content to the wrong people.

    Most caching plugins exclude these by default, but custom plugins don’t always play nice. If you’re seeing logged-in users get logged-out views, or vice versa, check your cache exclusions. You may need to add specific query keys or user roles to a denylist.

    The other gotcha: WordPress transients. These are temporary database entries that plugins use to store short-lived data—API responses, rate-limit counters, import progress. If your object cache is working, transients get stored in Redis instead of MySQL. That’s faster, but it also means transients disappear when you flush the cache. If a plugin relies on a transient sticking around, flushing can break it mid-operation.

    Check your transient usage with a plugin like Transients Manager. If you see hundreds of expired transients piling up, something’s writing to the cache inefficiently.

    Want more infrastructure breakdowns like this? Reply and tell us what hosting or performance topic to tackle next—or subscribe to catch every Tuesday tutorial.

  • WordPress database backups fail silently—here’s what to check

    Most solo operators discover their WordPress backup plugin failed the moment they need to restore something. By then, weeks or months of content, subscriber data, or order history are gone.

    Silent backup failures happen more often than platform dashboards admit. Plugins report green checkmarks while actual backups sit incomplete, corrupt, or missing entirely. This guide walks through the specific checks that catch failures before they cost you.

    Why WordPress backups fail without throwing errors

    Backup plugins rely on server resources—CPU, memory, execution time—that shared hosting environments throttle aggressively. When a backup job hits a limit, the process dies mid-export. The plugin logs a start time, assumes success, and moves on.

    Three common culprits:

    • PHP max_execution_time caps kill long-running database exports, especially on sites above 500 MB. The plugin times out before writing the full SQL dump.
    • Remote storage API failures break silently when Google Drive, Dropbox, or S3 tokens expire or hit rate limits. The plugin creates a local file but never uploads it.
    • Database table corruption during export causes incomplete dumps. The plugin writes a file, but restoring it fails because critical tables are missing rows.

    Most backup dashboards show “last backup: 2 hours ago” without validating file integrity or confirming the remote copy exists.

    Manual checks that catch silent failures

    Run these four checks monthly, or after any plugin/theme update that changes your database schema.

    1. Verify remote storage actually received the file. Log into your Dropbox, Google Drive, or S3 bucket. Sort by date modified. Confirm the most recent .zip or .sql file timestamp matches your plugin’s “last backup” log. File size should match or exceed previous backups unless you deleted content.

    2. Download and unzip the backup locally. Corrupt archives throw errors when you try to extract them. If the .zip opens but the database .sql file inside is under 1 MB on a site with years of posts, the export likely truncated.

    3. Scan the SQL dump for table completeness. Open the .sql file in a text editor. Search for CREATE TABLE statements. You should see wp_posts, wp_postmeta, wp_users, wp_options, and any custom tables your plugins add. Missing tables mean the backup won’t restore your site to its current state.

    4. Check plugin error logs, not dashboards. Most backup plugins write verbose logs to /wp-content/uploads/backups/ or similar. Look for lines containing “timeout,” “memory,” “curl error,” or “failed to write.” These warnings rarely surface in the admin UI.

    Fixing the three most common failure modes

    Execution timeouts: Increase max_execution_time in your php.ini or .htaccess, or split backups into separate database and file jobs. Plugins like UpdraftPlus let you run database and media exports independently, each staying under time limits.

    Remote storage auth failures: Reauthorize API connections every 90 days. Google Drive tokens expire; Dropbox app permissions get revoked during security audits. Test remote upload manually after reauthorizing.

    Database corruption: Run wp db repair via WP-CLI, or use phpMyAdmin’s repair function on tables flagged in error logs. Schedule this quarterly if you run high-traffic membership or e-commerce sites that hammer the database.

    When to test a full restore

    Backups you’ve never restored are backups you don’t actually have. Spin up a local environment or staging server once per quarter and restore your most recent backup end-to-end. Time the process. Note what breaks.

    Common restore gotchas: hardcoded domain URLs in serialized post meta, missing .htaccess rules, and plugins that store config outside the database (API keys in wp-config.php). Document the manual steps required so you’re not figuring them out during an outage.

    If restoring takes longer than 30 minutes or requires more than three manual fixes, your backup strategy isn’t production-ready. Simplify your plugin stack or move to managed WordPress hosting that handles backups at the server level.

    Set a calendar reminder right now: first Sunday of every month, download your latest backup and verify the SQL file opens and lists your tables. It takes four minutes and catches 90% of silent failures before they matter.

    What’s your backup horror story? Hit reply—we’re collecting operator war stories for a future piece on disaster recovery workflows that actually work under pressure.