Category: Hosting

  • 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.

  • WordPress CDN purge failures cost you traffic—here’s why

    WordPress CDN purge failures cost you traffic—here’s why

    You publish a time-sensitive post, hit save, and assume it’s live everywhere. But CDN cache purge doesn’t always work the way you think—and when it fails silently, you’re serving stale content to the readers who matter most.

    This isn’t a theoretical edge case. CDN purge failures happen daily across WordPress sites, especially when you’re running caching layers, security plugins, or multi-region delivery. The result: your homepage shows yesterday’s headline, your pricing page displays the old offer, and Google crawls a version of your post that’s missing the updated title tag.

    Here’s what actually happens when you hit publish, why purge requests fail, and how to verify your content is live before you share it.

    What happens when you publish a WordPress post

    When you click “Publish” in WordPress, several cache layers need to clear:

    • Object cache (Redis or Memcached, if enabled)
    • Page cache (WP Rocket, W3 Total Cache, LiteSpeed Cache)
    • CDN edge cache (Cloudflare, Bunny CDN, StackPath)
    • Browser cache (controlled by headers, not purge requests)

    Your caching plugin is supposed to send a purge request to the CDN when content changes. But purge requests are API calls—and API calls fail. The CDN might time out, the plugin might send the wrong URL format, or your firewall might block the purge webhook.

    The worst part: most purge failures are silent. WordPress doesn’t throw an error. The CDN logs the request but doesn’t retry. You think the post is live, but edge servers in London, Sydney, and Singapore are still serving the cached version for another hour.

    Why CDN purge requests fail

    URL mismatch between WordPress and the CDN. If your WordPress site uses http:// internally but your CDN serves https://, the purge request targets the wrong URL. Same problem if you’re using www. in one place and not the other. The CDN gets a purge request for a URL it doesn’t recognize, shrugs, and moves on.

    Wildcard purge disabled or misconfigured. Some CDN plans—especially free tiers—limit wildcard purges. If you update a post that appears on your homepage, archive pages, and RSS feed, you need to purge all of those URLs. If your plugin tries to purge /blog/* but your CDN only allows exact-match purges, nothing clears.

    Rate limiting on the CDN API. Cloudflare’s free plan allows 1,200 purge requests per day. That sounds like a lot until you’re editing a post multiple times, auto-saving drafts, and triggering purge requests on every revision. Hit the limit, and your next purge fails silently.

    Caching plugins that don’t talk to your CDN. Not every WordPress cache plugin supports every CDN. WP Rocket has built-in Cloudflare integration. LiteSpeed Cache works natively with LiteSpeed servers. But if you’re using a generic cache plugin with a smaller CDN, you might need a separate purge plugin—or manual API calls.

    How to verify your content is actually live

    Don’t trust the WordPress admin. Check the edge.

    The fastest way: open an incognito window and load your post URL. If you see the old version, the cache hasn’t cleared. But that only checks your edge location. If you’re in New York and your readers are in Berlin, you’re not seeing what they see.

    Better: use a CDN cache checker. Tools like RedBot or KeyCDN’s cache checker let you test specific URLs and see cache headers from multiple locations. Look for X-Cache: HIT or CF-Cache-Status: HIT in the response headers. If the Age: header shows a value higher than a few seconds after you published, you’re serving stale content.

    If you’re running Cloudflare, check the dashboard under Caching → Configuration → Purge Cache. You can manually purge individual URLs or the entire cache. If you do this every time you publish, your plugin isn’t working.

    How to fix purge failures before they cost you traffic

    Set your WordPress site URL to match your CDN exactly. Go to Settings → General and make sure WordPress Address (URL) and Site Address (URL) match the protocol and subdomain your CDN uses. If your CDN serves https://www.yoursite.com, both fields should say that. No exceptions.

    Enable wildcard purges or use cache tags. If your CDN supports cache tags (Cloudflare’s Cache-Tag header, Fastly’s Surrogate-Key), configure your caching plugin to tag pages by post ID, category, and archive type. When you update a post, purge by tag instead of URL. This clears every page that references that post, not just the post itself.

    Monitor purge success in your CDN logs. Cloudflare, Bunny, and most CDNs log purge requests. Check the logs weekly. If you see 4xx or 5xx responses, your purge requests are failing. Fix the integration before it compounds.

    Use a staging environment to test purge behavior. Before you publish a high-stakes post—launch announcement, pricing change, time-sensitive news—test the purge flow on staging. Publish the post, wait 30 seconds, check the live URL. If staging purges correctly, production should too. If not, you know before the post goes live.

    If you’re on a host that handles CDN integration for you—BigScoots and other managed WordPress hosts often do—verify that their purge automation covers all the URLs you care about. Ask support which cache layers clear automatically and which require manual intervention.

    When to bypass CDN cache entirely

    Some pages should never be cached at the edge: checkout pages, account dashboards, anything behind a login. Use cache bypass rules in your CDN config to exclude those URLs from edge caching entirely. That way, purge failures can’t hurt conversion flows.

    For time-sensitive content—live event updates, breaking news, flash sales—set a short TTL (60 seconds or less) instead of relying on purge. The CDN will refresh automatically, and you won’t lose traffic if a purge request fails.

    CDN purge isn’t fire-and-forget infrastructure. It’s an API call that fails more often than you think. The operators who verify purge success before sharing a post are the ones whose readers see the right content at the right time.

    Got a caching setup that’s failed you before? Reply and tell us what broke—we’ll cover it in a future issue.

  • WordPress multisite migration: what breaks and what doesn’t

    WordPress multisite migration isn’t the same as moving a single-site install. The network architecture means your database holds cross-site references, your media library spans domains, and your DNS has to route traffic through the primary domain before handing off to subsites.

    If you’ve ever moved a multisite network to a new host and watched half your images vanish or login redirects break, you know the pain. Here’s what actually breaks, what survives the move, and how to fix the common failure points before they cost you traffic.

    URL mappings break first

    Multisite stores site URLs in wp_blogs and wp_site tables. When you migrate, your old domain references stay hardcoded unless you run a search-and-replace across the network database.

    If you’re running subdomain multisite (like site1.yourdomain.com), every subsite has its own domain entry in wp_blogs. A standard WP migration plugin won’t catch these—it only updates siteurl and home in wp_options for the primary site.

    Run a network-wide search-and-replace using WP-CLI’s search-replace command with the --network flag, or use a plugin like Better Search Replace in network mode. Target the old domain string and replace it with the new one across all tables. Don’t skip serialized data—WordPress stores arrays and objects in the database, and a partial string replacement corrupts them.

    Media library paths don’t update automatically

    Multisite stores uploaded files in /wp-content/uploads/sites/{site_id}/ directories. When you migrate, the files move with the server, but image URLs in post content and media library metadata still point to the old domain.

    Your search-and-replace will catch most of these if you’re thorough. But if you’re moving from HTTP to HTTPS, or changing the subdirectory path (like moving from /blog/ to root), you’ll have orphaned image references that don’t render.

    Check wp_posts.guid and wp_postmeta for _wp_attached_file and _wp_attachment_metadata keys. These hold the full image paths and need manual correction if your directory structure changed. A broken guid won’t stop images from displaying, but it will break media library searches and attachment page URLs.

    Subdomain DNS has to route through the primary domain

    If you’re running subdomain multisite, your DNS needs a wildcard A record pointing *.yourdomain.com to your server IP. When you migrate hosts, that wildcard has to update to the new IP—and if your DNS propagation is slow, subsites will be unreachable even after the primary domain resolves.

    Check your DNS panel and confirm the wildcard record exists and points to the correct IP. If you’re using Cloudflare or another proxy, make sure the wildcard is set to DNS only (gray cloud) during migration, not Proxied (orange cloud). Proxied wildcard records can break subsite routing if the SSL certificate doesn’t cover all subdomains.

    If you’re moving to a managed WordPress host like BigScoots, ask support whether they handle wildcard DNS automatically. Some hosts provision it; others require manual setup.

    Plugin network activation states don’t transfer cleanly

    Multisite lets you activate plugins network-wide or per-site. When you migrate, the wp_sitemeta table holds network activation data, and wp_{site_id}_options tables hold per-site activation.

    If you’re moving between hosts with different PHP versions or server configurations, plugins that worked network-wide on the old host may fail silently on the new one. Check the active_sitewide_plugins key in wp_sitemeta after migration and deactivate any plugins that throw errors in the WordPress admin.

    Some plugins—especially caching and security plugins—store server paths in their settings. If your new host uses a different document root (like /var/www/html/ instead of /home/username/public_html/), those plugins will break until you regenerate their config files.

    What actually survives the move

    Your post content, user accounts, and plugin settings all migrate cleanly if you’re using a proper database export. The wp_users and wp_usermeta tables are network-global, so user logins work across all subsites immediately after the move.

    Theme files and uploads transfer intact if you copy the entire /wp-content/ directory. Just make sure file permissions are set correctly on the new host—644 for files, 755 for directories. If your new host uses a different user/group (like www-data instead of nobody), you’ll need to chown the files or uploads will fail.

    Test before you flip DNS

    Before you point your domain to the new host, test the migration using your server’s temporary URL or by editing your local /etc/hosts file to map your domain to the new IP.

    Log in to the network admin, visit a few subsites, upload a test image, and check whether plugins activate correctly. If anything breaks, you can fix it without taking the live site offline.

    Once you’re confident, update your DNS A records, wait for propagation, and flush any page caches. Multisite migrations take longer than single-site moves, but if you catch the URL mappings, media paths, and DNS routing, the network will come up clean.

    Got a multisite migration story? Reply with what broke for you—we’ll cover edge cases in a future issue.

  • How to run WordPress on DigitalOcean in 2026: every option compared

    How to run WordPress on DigitalOcean in 2026: every option compared

    Shared hosting is fine right up until the day it isn’t. For us, that day arrived when our host suspended a live site over a billing hiccup — taking the blog, the signup forms, the newsletter, and the paywall offline in one move, with no warning. If you run anything that earns money on WordPress, that is the moment you start looking at hosting you actually control.

    The obvious destination is a cloud provider like DigitalOcean: predictable pricing, real resources, no mysterious “you exceeded your plan” suspensions. But “put WordPress on DigitalOcean” turns out to have about six different answers depending on how much of the server you want to manage yourself. This is the honest comparison we wish we’d had — every option, what it costs in 2026, and which one actually fits a small operator running a handful of sites.

    First, understand the spectrum

    Every option below puts your site on the same underlying DigitalOcean infrastructure. What changes is who configures and maintains the server — nginx, PHP, MySQL, SSL certificates, caching, backups, and the cron jobs that make scheduled posts and newsletters actually fire on time. That maintenance layer is the entire decision.

    From most hands-on to least:

    • Do it all yourself — a bare droplet you configure by hand.
    • A control panel on your own droplet — you own the DigitalOcean account; a panel automates the server work.
    • Fully managed — a third party provisions and runs a DigitalOcean droplet for you.

    Option 1: The DigitalOcean 1-Click WordPress droplet

    DigitalOcean’s Marketplace has a “1-Click” WordPress image — a droplet pre-loaded with the full LEMP stack (Linux, nginx, MySQL, PHP) and WordPress already installed.

    The “one click” label oversells it. You still SSH into the server, run a setup script, point DNS, install an SSL certificate, and own every line of nginx and PHP config from then on. It is built around a single site, so running four sites means hand-rolling virtual hosts and databases. Cost: just the droplet — from about $12–24/month.

    Who it’s for: developers who genuinely enjoy server administration and want zero abstraction. Who it’s not for: anyone whose actual job is publishing, not sysadmin. The trap here is building a server only you understand — the day it breaks and you’re busy, there’s no support line and no UI.

    Option 2: A free control panel you self-host (CloudPanel)

    CloudPanel is a free, open-source control panel you install onto your own droplet. It gives you a clean web interface for managing nginx, PHP, MariaDB, and free SSL without paying a monthly panel fee.

    Cost: $0 plus the droplet. The catch is that you self-host and maintain the panel itself, and there’s no managed backups-to-cloud-storage or hand-holding support. It’s a strong middle ground for the technically confident who want to avoid recurring fees.

    Option 3: A managed control panel on your own droplet (the sweet spot)

    This is where most small operators land, and where we did. You create and own the DigitalOcean account and droplet. A control panel connects to it and automates everything: it provisions nginx, PHP, and the database, sets up free SSL on every new site, configures caching and cron correctly, and runs scheduled backups to cloud storage. You get full ownership and root access, but a polished UI removes about 95% of the sysadmin work.

    The main contenders in 2026, all of which support DigitalOcean, expose root access, and handle free SSL plus staging:

    Panel From (2026) WordPress-only? Built-in cloud backups Notes
    xCloud Free (1 server / 10 sites), $5/mo unlimited No (also Laravel/Node) Yes Newest, cheapest, DigitalOcean-native, generous free tier
    FlyWP $5/server/mo (bring your own server) WordPress-focused Yes Flat per-server pricing, no feature gating
    RunCloud ~$8–9/mo No (any PHP) Add-on Established, multi-framework, cheapest of the veterans
    SpinupWP $12/mo Yes Yes WordPress-native, best documentation, auto cron + caching
    Ploi ~€8 / $10/mo No (Laravel-leaning) Yes Developer-focused, strong CLI tooling
    GridPane Free (25 sites) up to $100/mo Yes Yes Agency-grade; powerful but overkill for a few sites

    The pattern is the same across all of them: you paste a DigitalOcean API token into the panel once, it spins up the droplet, and from then on you add sites from a dashboard instead of a terminal.

    Option 4: Fully managed (Cloudways)

    Cloudways — now owned by DigitalOcean — provisions and fully manages a DigitalOcean droplet on your behalf. Free migration, staging, automated backups, and a full control panel, with nothing for you to maintain at the server level. The trade-off is that the DigitalOcean relationship is owned by Cloudways, not you: it’s DigitalOcean infrastructure under a Cloudways account, starting around $11–14/month for a 1GB server.

    Who it’s for: operators who never want to think about a server and are happy not to hold the DigitalOcean account directly. DigitalOcean’s own App Platform can also run WordPress, but it’s container-oriented and awkward for traditional multi-site WordPress hosting — we’d skip it for this use case.

    The operator concerns that actually matter

    Spec sheets miss the things that bite you in production. Three to weigh:

    Cron reliability

    This is the quiet one. WordPress’s built-in scheduler (WP-Cron) only fires when someone visits your site. On a low-traffic site, that means scheduled newsletters and posts can fire late — or not at all — until a visitor happens to load a page. It’s a leading cause of “my scheduled email didn’t go out.” A proper server setup disables WP-Cron and replaces it with a real system cron job that runs every few minutes, deterministically. SpinupWP and the other WordPress-native panels configure this automatically; on a hand-rolled server you have to remember to do it. If reliable scheduled sending matters to you — and for a newsletter it’s the whole game — this alone justifies a managed panel.

    Email deliverability is separate from hosting

    A point of relief: how you send email doesn’t have to change when you move hosts. If you send through a dedicated service like Amazon SES, Resend, Postmark, or an SMTP provider, that keeps working regardless of where WordPress lives — you just carry your SPF, DKIM, and DMARC DNS records to wherever your DNS is managed. Moving hosts does not mean rebuilding your sending reputation.

    DNS and a free safety layer

    Moving to your own server is the natural moment to put Cloudflare (free) in front of your sites. You get easy DNS management, a global CDN, page caching, and DDoS protection at no cost — and it pairs neatly with bot-protection tools like Cloudflare Turnstile on your signup forms.

    How to move without downtime

    The migration itself is less scary than it sounds, because you never touch the live site until the very end:

    1. Provision the droplet and panel; create an empty WordPress install for each site.
    2. Copy each site across with a free migration plugin. Migrate Guru is the easy button — no size limits, runs on its own infrastructure rather than straining your server, and rewrites URLs automatically.
    3. Test each migrated site on a temporary URL before changing any DNS.
    4. Cut over DNS only once you’ve confirmed it works — lower your TTL first, then point the records at the new server. SSL re-issues automatically.
    5. Verify email sending, any payment webhooks, and scheduled jobs on the new host.
    6. Keep the old host for a grace week, then cancel.

    Do one low-stakes site first as a pilot, validate the whole flow end-to-end, then batch the rest once you trust it.

    So what should you actually pick?

    If you value… Pick
    Lowest cost, comfortable with a newer tool xCloud (free tier)
    Maturity, best docs, WordPress-native polish and support SpinupWP ($12/mo)
    Flat-rate middle ground FlyWP ($5/mo)
    Zero panel fee, fully self-hosted, hands-on CloudPanel (free)
    Never wanting to see a server again Cloudways

    For a small business that just got burned by an unreliable host, we’d weight reliability and support over saving a few dollars a month. A single $12–24/month DigitalOcean droplet comfortably runs several low-traffic WordPress sites, and a WordPress-native panel handles the cron and caching that prevent the exact failures that pushed us off shared hosting in the first place. The newer free-tier panels are genuinely compelling if you’d rather trial first and keep costs near zero — DigitalOcean’s $200 new-account credit covers months of droplet time while you decide.

    Whatever you choose, the principle is the same: own the account, own the data, and put a thin layer of automation between you and the terminal. That’s the difference between hosting that serves your business and hosting that can switch it off.

  • WordPress media library search: how it works and when it fails

    WordPress media library search: how it works and when it fails

    If you’ve ever uploaded 2,000 images to WordPress and then spent ten minutes hunting for the one screenshot you need, you’ve hit the wall of the default media library search. It’s not indexing what you think it’s indexing—and once you understand how it actually works, you can stop fighting it.

    What WordPress media search actually indexes

    The WordPress media library search looks at exactly three fields:

    • The original filename at upload time
    • The title field (auto-generated from the filename unless you edit it)
    • The alt text field (if you filled it in)

    That’s it. It does not search captions, descriptions, or any EXIF/IPTC metadata embedded in the image file itself. If you uploaded a photo called IMG_4387.jpg and added a detailed caption later, searching for words in that caption returns nothing.

    This surprises people who assume WordPress reads embedded keywords or folder structure. It doesn’t. The media library is flat, and search is string-matching against a narrow set of database fields.

    When it breaks down

    Three scenarios make the default search useless:

    Batch uploads from a camera or design tool. If your filenames are sequential numbers or random strings (Screenshot 2026-05-14 at 3.42.18 PM.png), you have no meaningful search surface. Unless you manually rename files before upload or edit the title field afterward, you’re scrolling.

    Stock photo libraries. Stock sites often deliver files with generic names like business-team-meeting-1234567.jpg. If you’ve downloaded fifty similar images over six months, searching “meeting” returns all of them in no useful order.

    Client handoffs or team uploads. When multiple people upload assets, filename conventions collapse. One person uses dashes, another uses underscores, a third uploads final_FINAL_v3.png. Search becomes a guess-and-scroll game.

    What to do instead

    If you’re running a content site that depends on reusing images—screenshots, product photos, infographics, author headshots—you need a layer on top of the default library.

    FileBird (free and pro versions) adds folder structure to the media library. You can nest folders, drag-and-drop files, and filter searches by folder. It doesn’t change how search works, but it narrows the haystack. The pro version ($29/year for unlimited sites) adds bulk folder assignment and better sorting.

    Media Library Assistant (free) lets you add custom taxonomy terms to media files. You can tag images with project names, content categories, or usage rights, then filter by those terms. It’s more powerful than folders but requires discipline—if your team doesn’t tag consistently, it creates noise instead of clarity.

    Search & Replace for Media is overkill for most solo operators, but if you’ve inherited a site with thousands of poorly-named files, it’s the fastest way to batch-rename based on upload date, folder, or pattern matching.

    The non-obvious fix: enforce filename hygiene at upload

    The simplest long-term solution is a pre-upload naming convention. Before dragging a file into WordPress, rename it to something searchable: headshot-jane-smith.jpg, screenshot-stripe-dashboard-fees.png, chart-open-rate-march-2026.png.

    This feels like extra work, but it’s five seconds per file. The alternative is five minutes of scrolling every time you need to reuse an asset. If you’re publishing multiple posts a week, the math tips fast.

    For teams, agree on a pattern: [content-type]-[subject]-[date].ext works for most workflows. The WordPress title field inherits the filename, so you get searchable metadata by default.

    When you actually need a DAM

    If you’re managing more than 10,000 media files, or if you’re running a membership site with gated content libraries, or if multiple editors need to pull from a shared asset pool with usage rights tracking, the WordPress media library isn’t the right tool anymore. You need a digital asset management system like Cloudinary, Bynder, or even a self-hosted solution like ResourceSpace.

    But for most solo operators and small teams, the problem isn’t the tool—it’s the lack of a naming system before upload. Fix that, and search starts working again.

    One question: how do you currently organize reusable images in WordPress—folders, tags, or just better filenames? Reply and let us know what’s working (or what’s driving you up the wall).

  • WordPress admin-ajax.php: what’s hammering your server and how to fix it

    WordPress admin-ajax.php: what’s hammering your server and how to fix it

    If you’ve ever looked at your WordPress server logs or run a performance profiler, you’ve probably seen admin-ajax.php appear hundreds—sometimes thousands—of times per minute. It’s not a bug. It’s a feature. But it’s also one of the most common reasons WordPress sites slow to a crawl under moderate traffic.

    Understanding what admin-ajax.php does, why it becomes a problem, and how to fix it without breaking your site is essential if you’re running a content site that depends on uptime and speed.

    What admin-ajax.php actually does

    WordPress uses admin-ajax.php to handle AJAX requests—asynchronous HTTP calls that let plugins and themes update parts of a page without reloading the whole thing. It’s the same file whether you’re logged in or not, and it handles everything from form submissions to live search to analytics pings.

    Every time a plugin needs to check something in the background—whether that’s a cart update, a like button, a notification counter, or a tracking pixel—it often routes through admin-ajax.php. The file itself is lightweight. The problem is what gets triggered after the request hits it.

    Each admin-ajax.php call loads the entire WordPress core, plus every active plugin, even if the request only needs one function from one plugin. That’s expensive. If you’re getting fifty admin-ajax.php calls per page load, you’re essentially booting WordPress fifty times per visitor.

    How to see if it’s a problem on your site

    Install Query Monitor (it’s free). Load a few pages on your site while logged out. Check the “AJAX” panel. If you see dozens of requests firing on every page, or if the same hook is being called repeatedly, you’ve got a problem.

    Another test: open your browser’s network inspector (F12 → Network tab). Load your homepage. Filter by “admin-ajax.php.” Count how many requests fire. Anything above five is worth investigating. Anything above twenty is a red flag.

    Common culprits include:

    • Social sharing plugins that ping counters on every page load
    • Live chat widgets checking for new messages every few seconds
    • Analytics plugins logging events in real time
    • WooCommerce or membership plugins polling session data
    • Page builders with “live edit” preview modes running in the background

    Three ways to reduce the load

    1. Replace the plugin. If a plugin is hammering admin-ajax.php and there’s a leaner alternative, switch. Social share counters are a classic example—most don’t need live data. Cache the counts once per hour and serve static numbers.

    2. Disable unnecessary AJAX calls. Many plugins let you turn off real-time features. WooCommerce, for example, has a “cart fragments” feature that updates the cart icon via AJAX on every page. If you don’t show a persistent cart widget, you can disable it with a one-line code snippet:

    add_action('wp_enqueue_scripts', function() { wp_dequeue_script('wc-cart-fragments'); }, 11);

    That single change can cut admin-ajax.php requests by 70% on WooCommerce sites.

    3. Move AJAX calls to the REST API. If you control the plugin or theme code, rewrite AJAX handlers to use WordPress’s REST API instead of admin-ajax.php. REST endpoints don’t load the admin environment, so they’re faster and easier to cache. This requires developer work, but it’s the cleanest long-term fix.

    When caching makes it worse

    Most page caching plugins don’t cache admin-ajax.php by default, because the responses are often user-specific. That means every AJAX call bypasses your cache and hits PHP directly.

    If your site is getting 10,000 page views per day and each page fires 15 admin-ajax.php requests, that’s 150,000 uncached PHP executions. Your server will feel it.

    Some hosts—particularly managed WordPress hosts—rate-limit admin-ajax.php to prevent abuse. If you hit that limit, parts of your site just stop working. Forms don’t submit. Buttons don’t respond. Visitors leave.

    The fix isn’t to increase the limit. It’s to reduce the requests.

    One non-obvious tip

    If you’re stuck with a plugin that won’t stop calling admin-ajax.php, and you can’t replace it, consider splitting the plugin’s functionality onto a separate subdomain or a headless API. For example, if you’re running a live chat widget that pings admin-ajax.php every three seconds, host the chat on chat.yoursite.com and let it hit a lightweight Node.js endpoint instead of your WordPress server.

    It’s more work up front, but it isolates the performance hit and keeps your main site fast.

    What’s slowing down your WordPress site right now? Reply with your biggest performance headache—I’ll cover it in a future issue. And if you’re shopping for a host that understands this stuff out of the box, BigScoots is worth a look.

  • WordPress database table prefixes: security theater or real protection?

    WordPress database table prefixes: security theater or real protection?

    Open any WordPress hardening guide and you’ll see the same advice: change your database table prefix from wp_ to something custom during installation. The logic sounds reasonable—if attackers don’t know your table names, they can’t exploit them. But after a decade of this being standard security advice, it’s worth asking whether it actually prevents anything meaningful.

    What the prefix actually does

    WordPress stores everything—posts, users, options, metadata—in a MySQL or MariaDB database. By default, tables are named wp_posts, wp_users, wp_options, and so on. The wp_ part is the prefix, defined in wp-config.php during installation.

    Changing it to something like xyz_ or j4k_ means your tables become xyz_posts, j4k_users, etc. The theory: SQL injection attacks that hardcode wp_users will fail because that table doesn’t exist in your database.

    In practice, modern SQL injection exploits don’t guess table names. They use SHOW TABLES or query the information_schema database to list everything, regardless of prefix. If an attacker has SQL injection access, your custom prefix buys you nothing—they’ll enumerate your schema in milliseconds.

    Where it breaks things

    Custom prefixes introduce friction in three places:

    Plugin compatibility. Most plugins handle prefixes correctly using WordPress’s $wpdb class, but older or poorly-maintained plugins sometimes hardcode wp_ in raw queries. You won’t know until something breaks in production.

    Manual database queries. If you ever need to run a direct SQL query—fixing a broken migration, bulk-updating post metadata, cleaning spam—you have to remember your custom prefix. Documentation and Stack Overflow answers assume wp_, so you’re translating every example.

    Migrations and cloning. Tools like WP Migrate DB, All-in-One WP Migration, and even hosting-panel cloners expect wp_ by default. Custom prefixes mean extra configuration steps, and if you’re moving between staging and production frequently, that’s friction you’ll feel every time.

    What actually hardens WordPress databases

    If your goal is to prevent database compromise, three things matter more than your table prefix:

    Separate database users with limited privileges. Your WordPress database user should only have SELECT, INSERT, UPDATE, and DELETE on its own database—not DROP, CREATE, or access to other databases. Most shared hosts set this up correctly, but if you’re on a VPS or managing your own MySQL instance, check SHOW GRANTS FOR 'your_db_user'@'localhost'; to confirm.

    Parameterized queries in custom code. If you’re writing your own plugin or theme functions that touch the database, use $wpdb->prepare() for every query with user input. This prevents SQL injection at the source, regardless of table names.

    Regular patching. Most WordPress database exploits come through outdated plugins, not core. If you’re running auto-updates for minor releases and reviewing plugin changelogs before major updates, you’re ahead of 80% of sites. A custom prefix won’t save you from a known vulnerability in a form plugin that’s six months behind.

    The verdict

    Changing your database prefix isn’t harmful—it just doesn’t deliver the security benefit it’s credited with. If you’re setting up a new site and the installer asks, there’s no reason not to customize it. But if you’re migrating an existing site or running a staging workflow where wp_ simplifies things, you’re not opening a meaningful vulnerability by leaving it default.

    Security checklists love to include it because it’s easy to explain and feels like hardening. But the threat model it addresses—automated scripts blindly guessing table names—hasn’t been relevant since 2010. Real attacks enumerate your schema or exploit application-layer vulnerabilities, and your prefix is irrelevant to both.

    Focus on database user permissions, parameterized queries, and keeping plugins updated. Those three will stop actual attacks. A custom prefix just makes your wp-config.php feel more secure.

    What’s your take? If you’ve seen a real-world case where a custom prefix stopped an attack, reply—I’d genuinely like to know. Otherwise, subscribe below for weekly deep-dives on the tools and tactics that actually move the needle for solo operators.

  • WordPress plugin auto-updates: which ones break sites in production

    WordPress plugin auto-updates: which ones break sites in production

    WordPress added automatic plugin updates in 2020. The pitch was simple: set it once, never worry about security patches again. But every operator who’s enabled auto-updates across the board has learned the same lesson—some plugins don’t play well with unattended updates, and the ones that break tend to break hard.

    The question isn’t whether to use auto-updates. It’s which plugins can be trusted to update themselves, and which need human review before they touch production.

    The plugin categories that auto-update safely

    Security plugins, spam filters, and utilities that don’t touch your front-end rendering are usually safe bets. Plugins like Wordfence, Akismet, and Redirection rarely introduce breaking changes because their scope is narrow and their update patterns are conservative.

    Same goes for plugins that handle single, well-defined tasks: backup tools, uptime monitors, analytics trackers. If the plugin doesn’t interact with your theme, doesn’t hook into checkout flows, and doesn’t modify post content, auto-update risk is low.

    I’ve run auto-updates on Wordfence, UpdraftPlus, and MonsterInsights across a dozen sites for two years without a single incident. These plugins update frequently, but they’re built with backwards compatibility in mind.

    The plugin categories that break silently

    Page builders, membership plugins, and ecommerce extensions are the opposite. These plugins hook into WordPress core rendering, modify database schemas, and depend on specific PHP versions or third-party APIs. When they update, they can break layouts, disable checkout, or lock users out of gated content.

    Elementor and WooCommerce are notorious for this. A minor version bump can introduce a CSS conflict that destroys mobile navigation, or a database migration that fails halfway through and leaves orders in limbo. Auto-updating these plugins on a revenue-generating site is a gamble.

    Same goes for plugins that modify admin workflows or add custom post types. If a plugin changes how your CMS behaves, you need to test the update in staging before it touches production. Auto-updates remove that step.

    The real cost of a bad auto-update

    A broken plugin doesn’t just throw an error message. It can take down your entire site, disable your email opt-in forms, or break your payment processor. If that happens at 11pm on a Friday, you’re either rolling back blind or losing revenue until Monday morning.

    I’ve seen a single WooCommerce auto-update disable checkout for six hours because the new version required PHP 7.4 and the host was still running 7.3. The plugin didn’t throw a warning—it just silently failed. The site owner only noticed because a customer emailed to say the cart was broken.

    That’s the problem with auto-updates: they assume your environment is compatible, your theme won’t conflict, and your custom code won’t break. None of those assumptions hold on a real site.

    How to decide which plugins get auto-updates

    Start by auditing your plugin list. Group them into three buckets:

    • Critical path: Plugins that handle revenue, user access, or content delivery. These need manual updates with staging tests first.
    • Front-end rendering: Plugins that modify your theme, inject CSS, or change layout. Auto-update risk is high.
    • Background utilities: Plugins that run cron jobs, log data, or handle security. These are usually safe to auto-update.

    For the critical-path plugins, disable auto-updates and set a monthly calendar reminder to update manually. For front-end plugins, test updates in a staging environment first—most hosts offer staging as a built-in feature now.

    For background utilities, enable auto-updates but configure uptime monitoring so you know immediately if something breaks. Tools like Jetpack Monitor or UptimeRobot are free and will email you within five minutes of downtime.

    The staging workflow that catches problems early

    If you’re running a content site with ad revenue or a membership site with gated access, you need a staging environment. Clone production once a week, enable auto-updates on staging only, and let it run for 48 hours. If nothing breaks, manually apply the same updates to production.

    This workflow adds one manual step, but it catches breaking changes before they hit live traffic. Most managed WordPress hosts—BigScoots, Kinsta, WP Engine—offer one-click staging environments and automated sync tools.

    If your host doesn’t support staging, use a plugin like WP Staging to create a local clone. It’s not as clean as a proper staging server, but it’s better than testing updates on production.

    The rule is simple: if a plugin touches revenue or user experience, test it first. If it runs in the background and doesn’t modify your front end, let it update itself. Everything else is a judgment call based on how much downtime you can tolerate.

    Want more WordPress infrastructure breakdowns like this? Reply with the hosting or plugin topic you’re trying to solve—we’ll cover it in a future issue.