Category: Hosting

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

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

    WordPress plugin auto-updates: when to enable and when to pause
    Photo by Souvik Banerjee on Unsplash

    WordPress added per-plugin auto-update toggles in version 5.5, giving operators granular control over what updates while they sleep and what waits for manual approval. The feature sits quietly in the plugin list, easy to miss—but using it correctly can prevent broken checkout pages, missing forms, and late-night rollback sessions.

    Most solo operators treat auto-updates as binary: all on or all off. That leaves money on the table. The right approach is selective, based on each plugin’s role and the vendor’s track record.

    How the feature works

    Each plugin row in your WordPress dashboard includes an “Enable auto-updates” link. Toggle it on, and WordPress checks twice daily for new versions and installs them without intervention. The core system sends an email notification after each auto-update completes, listing what changed and linking to the changelog (checked November 2026, WordPress.org plugin documentation).

    Auto-updates run during the standard WordPress cron cycle, typically in low-traffic hours if your server respects the schedule. If an update fails mid-install, WordPress attempts to roll back to the previous version and logs the error. It doesn’t always succeed—especially if the failure involves database schema changes.

    The feature works independently of theme auto-updates and core auto-updates. You can run all three, none, or any combination.

    Which plugins to auto-update

    Enable auto-updates for plugins that meet all three criteria: low site-integration complexity, frequent security patches, and a vendor with a clean deployment history.

    Security and anti-spam plugins top the list. Akismet, Wordfence, and similar tools release patches within hours of discovering new attack vectors. Waiting even a day to manually update exposes your comment forms and login pages. These plugins rarely touch your theme or content rendering, so breakage risk stays low.

    Performance and caching plugins are the opposite. A caching plugin update can silently break CSS delivery, JavaScript execution, or CDN purging. If you run WP Rocket, W3 Total Cache, or similar, keep auto-updates off and test each release on a staging site first. The performance gain from a new version rarely justifies the risk of a broken front-end during a product launch.

    Page builders and form plugins sit in the middle. Elementor, WPForms, and Gravity Forms push updates often, and most are stable—but a bad release can take your lead-capture forms offline or scramble post layouts. If your business depends on a single landing page or checkout flow, disable auto-updates and schedule manual testing weekly. If you run dozens of pages and can tolerate occasional layout shifts, auto-updates save time.

    SEO and analytics plugins—Yoast, Rank Math, MonsterInsights—are safe to auto-update unless you’ve customised their output with filters or hooks. They touch metadata and tracking scripts, not front-end rendering, so breakage is rare. The exception: if you’ve hard-coded schema markup or custom Open Graph tags that conflict with plugin defaults, test updates manually.

    One non-obvious trick: auto-update only after vendor testing

    Many plugin vendors release updates in waves. They push to a small percentage of installs first, monitor error reports for 24–48 hours, then roll out globally. If you enable auto-updates, you’re in the first wave by default.

    To stay in the second wave—where critical bugs have already been patched—leave auto-updates off and manually update three days after a release appears in your dashboard. Check the plugin’s WordPress.org support forum or changelog for version increments like 3.2.1 following 3.2.0. If you see a point-release within 72 hours, the initial version had issues.

    For mission-critical plugins, subscribe to the vendor’s release notes via RSS or email. Most publish a changelog feed at pluginname.com/changelog or link it from their WordPress.org page. Skim each update summary before toggling auto-updates back on.

    When auto-updates cause problems

    Two failure modes happen often enough to plan for: email notification fatigue and silent cron failures.

    If you run fifteen plugins with auto-updates enabled, you’ll receive fifteen separate emails each time WordPress checks for updates—sometimes multiple per day if vendors release patches quickly. The volume trains you to ignore the messages, so you miss the one notification warning that a payment plugin failed to update and left your checkout broken. Consolidate auto-update notifications by filtering them into a dedicated email folder and reviewing it once weekly, or disable the emails entirely and check the plugin list manually.

    Silent cron failures happen when your hosting provider disables WordPress’s built-in cron and replaces it with a server-level cron job that doesn’t fire reliably. Auto-updates queue but never execute. Check your wp-config.php file for the line define('DISABLE_WP_CRON', true);—if present, confirm your host has configured a replacement cron job at wp-cron.php and that it runs at least twice daily (checked November 2026, WordPress developer cron documentation).

    If you’re running WordPress on BigScoots or a similar managed host, ask support to confirm cron job frequency before relying on auto-updates for time-sensitive security patches.

    One last thing: We publish a new operator-focused guide like this one every day. Subscribe to One Two Three Send to get tomorrow’s in your inbox.

  • WordPress CDN cache purge: when stale pages linger after publish

    WordPress CDN cache purge: when stale pages linger after publish

    WordPress CDN cache purge: when stale pages linger after publish
    Photo by Szabo Viktor on Unsplash

    You update a post, hit Publish, and check the live page—but the old version is still there. You clear your browser cache. Still stale. You wait five minutes. Still wrong.

    The culprit is usually your CDN’s edge cache, and the purge signal your WordPress site sent either never arrived, arrived too late, or targeted the wrong URL variant.

    Here’s how CDN cache invalidation actually works, where the automation breaks, and what to verify when pages stay stuck.

    How WordPress tells a CDN to purge

    When you publish or update a post, most WordPress caching plugins send a purge request to your CDN. The request typically targets:

    • The post or page URL itself
    • The homepage
    • Any archive pages (category, tag, author) that list the post
    • RSS feeds

    Popular plugins like WP Rocket, W3 Total Cache, and LiteSpeed Cache include CDN purge integrations for Cloudflare, StackPath, KeyCDN, and others. Each uses the CDN’s API to issue a cache invalidation command.

    The CDN then propagates that purge across its edge nodes. Depending on the provider and purge method, this can take anywhere from a few seconds to several minutes.

    Where purge requests fail

    The most common failure points:

    API credentials expired or misconfigured. If your plugin is using an outdated API token, the purge request returns a 401 or 403 error—but WordPress doesn’t surface that to you. Check your CDN integration settings and regenerate tokens if you’ve rotated passwords or switched accounts.

    URL mismatch between WordPress and the CDN. If your WordPress site uses http:// internally but your CDN caches https:// URLs, or if you’ve changed your permalink structure without flushing the CDN, purge requests may target URLs that don’t match what’s actually cached. Cloudflare’s API, for example, treats example.com/post/ and example.com/post (with and without trailing slash) as distinct cache keys unless you’ve set a page rule to normalize them.

    Partial purge vs. full purge. Many plugins default to purging only the updated post and homepage to avoid rate limits. If your post appears on a paginated archive (e.g., page 2 of a category), that archive page may remain cached with the old excerpt or publish date. Some CDNs charge per purge request or throttle bulk invalidations, so plugins err on the side of conservative purging.

    CDN caching HTML despite cache-control headers. If your CDN is set to ignore origin cache headers and enforce its own TTL, purge requests may succeed but the CDN repopulates the cache immediately from a stale upstream layer (like Varnish or Redis object cache on your host). Check your CDN’s cache rules to ensure it respects Cache-Control: no-cache or must-revalidate directives for dynamic pages.

    What to check first

    When a page won’t update:

    Confirm the CDN is the bottleneck. Bypass the CDN by hitting your origin server directly (use your host’s IP or a hosts-file override). If the page is current at origin but stale through the CDN, the purge failed.

    Check the plugin’s purge log. WP Rocket and LiteSpeed Cache log purge events in their debug modes. Enable logging, update the post again, and review whether the plugin attempted a purge and what response it received. Missing API errors here are a smoking gun.

    Verify which URLs were purged. Most CDN dashboards (Cloudflare’s under Caching → Configuration → Purge Cache history, checked November 2026) show recent purge requests. Compare the purged URLs to the actual cached URLs. If you see /blog/post-name/ purged but your CDN cached /blog/post-name, trailing-slash normalization is broken.

    Test with a manual full purge. Log into your CDN dashboard and purge everything. If the page updates immediately, the issue is with selective purging logic—either the plugin isn’t targeting the right URLs, or your CDN’s partial purge propagation is slow.

    A non-obvious fix

    If you’re using Cloudflare with a page rule that caches everything, double-check the Cache Level setting. Cloudflare’s Ignore Query String level treats example.com/post?v=123 and example.com/post as the same cache object, which can cause purge misses if your theme or plugin appends query strings for versioning. Switch to Standard or add an explicit purge for query-string variants.

    For hosts running Varnish or Nginx FastCGI cache in front of WordPress, coordinate purges at both layers. Your CDN plugin may purge the CDN edge but leave Varnish’s copy intact; the CDN refetches stale HTML from Varnish, and you’re back where you started. Tools like Varnish HTTP Purge (checked via the WordPress plugin directory, November 2026) can integrate with your CDN plugin to purge both simultaneously.

    Cache invalidation remains one of the hardest problems in distributed systems. When it breaks, start at the origin, trace the purge path, and confirm every hop actually cleared.

    Hit reply if you’ve found a CDN purge edge case we didn’t cover—we’ll feature operator solutions in a future issue.

  • WordPress multisite subdomain wildcards: DNS, SSL, and provisioning

    WordPress multisite in subdomain mode promises a clean architecture: every new site gets its own subdomain, and the platform handles provisioning automatically. In practice, three layers—DNS wildcards, SSL certificate issuance, and web-server routing—each introduce failure points that most operators discover only after the first user tries to access a freshly created site and sees a browser error.

    This guide walks through how wildcard subdomain provisioning actually works, where it breaks, and what to check when a new site won’t load.

    DNS wildcard records: what they match and what they don’t

    A wildcard DNS A record like *.example.com tells resolvers that any subdomain—alice.example.com, bob.example.com—should resolve to the same IP address as the multisite installation. You add this record once in your DNS provider’s control panel, pointing to your server’s public IP.

    Wildcards match only one level deep. If you need nested subdomains like project.alice.example.com, the wildcard *.example.com won’t cover it; you’d need *.alice.example.com or another arrangement. Most multisite installations stick to single-level subdomains to avoid this complexity.

    DNS propagation is usually fast—under an hour for most resolvers—but caching at the user’s ISP or device can mean stale lookups persist for the original TTL. Set a low TTL (300 or 600 seconds) on the wildcard record before launching new sites if you expect rapid iteration.

    SSL wildcard certificates: issuance, renewal, and Let’s Encrypt limits

    Once DNS resolves, the browser requests HTTPS. A wildcard certificate like *.example.com covers all first-level subdomains in a single cert. Let’s Encrypt supports wildcard issuance via DNS-01 challenge, which requires your host or control panel to programmatically update a _acme-challenge TXT record during validation (documented in the Let’s Encrypt challenge-types guide, checked November 2026).

    Not every managed WordPress host automates DNS-01. If your host uses HTTP-01 validation instead, you’ll need to request individual certificates for each subdomain, which breaks the seamless provisioning model. Check your host’s multisite documentation before committing to subdomain mode.

    Let’s Encrypt rate limits allow 50 certificates per registered domain per week (rate-limits page, checked November 2026). If you’re spinning up dozens of subsites in a short window and relying on individual HTTP-01 certs, you’ll hit the cap and new sites will serve warnings until the counter resets. Wildcard certs bypass this entirely, because one cert covers the whole namespace.

    Web-server routing: how Nginx and Apache parse Host headers

    After DNS and SSL succeed, the request reaches your web server. WordPress multisite relies on the Host header to identify which subsite the visitor wants. Nginx and Apache both support wildcard server blocks or virtual hosts, but the configuration syntax differs and misconfigurations silently fall back to the default site.

    In Nginx, a server_name directive like *.example.com matches any subdomain. If you’re using a caching layer or CDN in front of Nginx, make sure the Host header passes through unmodified; some CDNs rewrite it to the origin hostname, which breaks multisite routing.

    Apache’s ServerAlias directive accepts wildcards similarly. Both servers also respect the order of virtual-host definitions: if the wildcard block appears after a more specific block, requests may match the wrong site. Place the wildcard configuration last in your include order to ensure fallback behavior works as expected.

    Auto-provisioning failures: what to check when a new site won’t load

    When a user reports that a newly created subsite shows a certificate error, connection timeout, or default Apache page, work backwards through the stack:

    • DNS: Use dig alice.example.com or an online resolver tool to confirm the wildcard record resolves to the correct IP. If it doesn’t, check your DNS provider’s propagation status and TTL settings.
    • SSL: Check the certificate presented by the subdomain with openssl s_client -connect alice.example.com:443 -servername alice.example.com. If it’s a self-signed cert or doesn’t include the subdomain in the SAN list, your wildcard cert didn’t issue or isn’t installed correctly.
    • Web server: Review access logs for the subdomain request. If the log entry shows a 200 but the browser sees an error, caching or a CDN is serving stale content. If the log shows a 404 or no entry appears, the server block isn’t matching; review your Nginx or Apache configuration syntax.
    • WordPress: Confirm the subsite exists in the network admin dashboard and that its domain matches exactly what the user is requesting. Trailing slashes, www prefixes, and protocol mismatches all cause routing failures.

    When to avoid subdomain mode altogether

    Subdomain multisite makes sense when you want clean namespace separation and don’t mind the DNS and SSL overhead. If your hosting environment doesn’t support wildcard DNS automation or you’re on a shared host without root access to install wildcard certificates, subdirectory mode (example.com/alice) is simpler and avoids all three provisioning layers.

    Subdirectory mode also bypasses browser cookie-isolation issues: cookies set on example.com are visible to all subdirectory sites, which can simplify single sign-on but complicates multi-tenant privacy. Subdomain mode isolates cookies by default, because alice.example.com and bob.example.com are separate origins.

    Running WordPress multisite? Reply with the weirdest provisioning failure you’ve debugged—we’ll feature operator war stories in an upcoming issue.

  • WordPress media library pagination: where large libraries slow down

    WordPress media library pagination: where large libraries slow down

    WordPress media library pagination: where large libraries slow down
    Photo by Pankaj Patel on Unsplash

    If you’ve been uploading images, PDFs, and video thumbnails to WordPress for more than a year, you’ve probably noticed the media library start to drag. The grid takes longer to load. Scrolling feels janky. Search stops returning results fast enough to be useful.

    The problem isn’t your hosting—it’s how WordPress handles pagination when your media library grows past a few thousand files.

    How WordPress loads the media library

    When you open the media library, WordPress runs a database query to fetch attachments. By default, it returns 40 items per page in grid view, or as many as your screen size allows in list view. Each page load triggers a new query with an OFFSET clause that tells the database how many rows to skip before returning results.

    For the first few pages, this works fine. But as your library grows—say, past 3,000 to 5,000 files—every query has to scan and skip thousands of rows before it can return the 40 you actually want to see. The database does more work, and load times climb.

    WordPress core doesn’t index attachment queries by upload date or file type in a way that speeds up deep pagination. According to the WordPress Query class reference (checked November 2026), the WP_Query object used for media relies on the same posts table structure as everything else, and large offsets are expensive regardless of table size.

    When it breaks

    You’ll notice the slowdown around these thresholds:

    • 3,000–5,000 files: Grid view starts to lag when you scroll past page 10.
    • 10,000+ files: Searches take multiple seconds. Filtering by date or media type doesn’t help much because the query still scans the full table.
    • 25,000+ files: The media library becomes nearly unusable in the WordPress admin. Some hosting environments will time out before returning results.

    The exact breaking point depends on your database server, PHP memory limit, and whether your host uses object caching. But the pattern holds: offset-based pagination doesn’t scale.

    What to do about it

    Use a media library plugin that indexes differently. Tools like Media Library Assistant (checked November 2026) let you filter and sort by custom taxonomies, which can bypass some of the worst query patterns. Others, like Enhanced Media Library (checked November 2026), add categories and MIME-type filters that rely on indexed joins instead of table scans.

    Offload storage and serve files from a CDN or object storage service. This doesn’t fix pagination, but it does reduce the number of database rows WordPress has to manage. Plugins like WP Offload Media (checked November 2026) move your files to S3 or compatible services and leave only metadata in the WordPress database. Fewer rows means faster queries, even with large offsets.

    Archive old media. If you have thousands of images from posts you published years ago, export them, back them up, and delete the attachments from WordPress. You can re-upload individual files if you ever need to update an old post. This is manual work, but it’s the fastest way to shrink your media library if you don’t need random access to every file you’ve ever uploaded.

    Increase your per-page limit (carefully). Loading 100 or 200 items per page reduces the number of deep-offset queries you’ll trigger, but it also increases memory usage and can make the initial page load slower. Test this on a staging site before you push it to production. You can adjust the number in Screen Options at the top of the media library page.

    One non-obvious tip

    If you’re building a content site that publishes daily and embeds multiple images per post, budget for media-library maintenance from the start. Set a yearly reminder to audit and archive files, or commit to offloading storage before you hit 5,000 attachments. Fixing a slow media library after the fact is harder than preventing the problem in the first place.

    Most solo operators don’t think about database query performance until it’s already a problem. Treat your media library like you treat your email list: clean it regularly, and don’t let it bloat.

    Hit reply if you’ve found a better way to handle large WordPress media libraries—we’ll share the best answers in a future issue.

  • WordPress revision storage: how much disk each draft really uses

    WordPress revision storage: how much disk each draft really uses

    WordPress revision storage: how much disk each draft really uses
    Photo by Nick on Unsplash

    WordPress saves a complete copy of your post content every time you hit save. Not a delta. Not a diff. The entire post body, custom fields, and metadata—duplicated in full and written to wp_posts with a row type of revision.

    For a solo operator publishing three posts a week, this rarely matters. But if you’re running a content site with multiple contributors, long-form articles, or dozens of custom fields per post, revision storage can balloon into hundreds of megabytes—sometimes gigabytes—of database bloat that slows queries, inflates backups, and complicates migrations.

    Where revisions live and how they grow

    WordPress stores every revision as a separate row in the wp_posts table, linked to the parent post by post_parent. Each revision carries the full post_content, post_title, post_excerpt, and timestamp. If you use custom fields or a page builder that serializes layout data into post meta, those get duplicated too—in wp_postmeta, keyed to the revision ID.

    By default, WordPress keeps every revision forever. A 5,000-word post edited twenty times generates twenty full copies of that content. If you’re embedding base64 images in custom fields or storing JSON layout blocks, each revision can exceed 100 KB. Twenty revisions × 100 KB = 2 MB for one post.

    Multiply that across a year of publishing and you’re looking at meaningful disk usage—and slower SELECT queries whenever WordPress scans wp_posts without an index filter on post_type.

    How to cap or disable revisions

    You can limit the number of revisions WordPress keeps by adding a constant to wp-config.php:

    define( 'WP_POST_REVISIONS', 5 );

    This keeps the five most recent revisions per post and discards older ones on the next save. Setting it to false disables revisions entirely, though that also removes the ability to roll back accidental edits—useful for solo operators who publish fast and rarely need an undo buffer.

    To disable autosave (which triggers a revision every 60 seconds by default), add:

    define( 'AUTOSAVE_INTERVAL', 300 );

    That pushes the interval to five minutes. You can’t disable autosave completely without a plugin, but stretching the interval cuts down on duplicate saves during editing sessions.

    The WordPress Codex documents both constants under Revisions (checked November 2026).

    When to prune and how much you’ll recover

    If you’ve been running WordPress for years without a revision limit, you can reclaim disk space by deleting old revisions. Plugins like WP-Optimize and Advanced Database Cleaner offer one-click pruning, but you can also run a manual query:

    DELETE FROM wp_posts WHERE post_type = 'revision';

    Follow it with:

    DELETE pm FROM wp_postmeta pm LEFT JOIN wp_posts p ON pm.post_id = p.ID WHERE p.ID IS NULL;

    That clears orphaned post meta tied to deleted revisions. On a 500-post site with twenty revisions per post, you might recover 200–500 MB of database storage. On larger content operations, the savings can hit multiple gigabytes.

    After deletion, run OPTIMIZE TABLE wp_posts; and OPTIMIZE TABLE wp_postmeta; to reclaim the freed space and rebuild indexes. Most managed WordPress hosts (including BigScoots) run nightly optimization, but manual pruning is faster if you’re migrating or cloning a site.

    One non-obvious consequence: backup size and restore time

    Revisions don’t just eat disk—they inflate every database backup. If your nightly backup routine dumps the entire database to S3 or a remote server, a bloated wp_posts table means longer backup windows, higher transfer costs, and slower restores when you need to roll back.

    A 2 GB database with 1.5 GB of revisions takes three times as long to restore as a pruned 500 MB copy. If you’re running automated staging-site clones or testing migrations, trimming revisions before export cuts minutes—sometimes hours—off the process.

    If you’re managing a high-frequency content site, set WP_POST_REVISIONS to 3–5 in wp-config.php today. You’ll keep enough history to recover from mistakes without letting old drafts compound into database drag. And if you’ve never pruned, run a one-time cleanup and watch your backup size drop.

    Got a WordPress infrastructure question? Reply and we’ll cover it in a future issue.

  • WordPress REST API rate limits: where they sit and how to raise them

    WordPress REST API rate limits: where they sit and how to raise them

    WordPress REST API rate limits: where they sit and how to raise them
    Photo: Rami via Wikimedia Commons (Public domain)

    If you run a content site that integrates with external services—publishing tools, mobile apps, automation chains—you’re probably hitting the WordPress REST API dozens or hundreds of times a day. And at some point, you’ll see a 429 response or a cryptic timeout, even though WordPress core itself ships with no built-in rate limit on REST endpoints.

    The throttle isn’t coming from WordPress. It’s coming from the layers wrapped around it: your hosting environment, Web Application Firewall, caching plugin, or a security tool you installed six months ago and forgot about.

    Here’s where REST API rate limits actually live, how to identify which one is blocking you, and when raising the cap makes sense.

    WordPress core has no REST API rate limit

    WordPress itself does not enforce a request-per-minute cap on /wp-json/ endpoints. The REST API documentation (checked October 2026) confirms this: authentication and capability checks exist, but no native throttle.

    If you’re seeing rate-limit responses, they’re being injected upstream or by a plugin. The most common culprits:

    • Web Application Firewall (WAF) rules at Cloudflare, Sucuri, or your host’s edge
    • Hosting-level throttles applied to admin-ajax.php and /wp-json/ paths
    • Security plugins like Wordfence, iThemes Security, or All In One WP Security
    • Caching plugins with REST API exclusion rules that block rather than cache

    Each throttles differently, logs differently, and requires a different fix.

    Hosting-level limits: check your control panel first

    Managed WordPress hosts—especially those optimised for high traffic—often apply rate limits to resource-heavy endpoints. These caps protect shared infrastructure but aren’t always documented in your plan details.

    At BigScoots and similar managed hosts, REST API requests may be capped at 60–120 requests per minute per IP address. If you’re running a headless frontend, mobile app, or Zapier automation that polls frequently, you’ll hit this quickly.

    Check your hosting dashboard for firewall or security settings. Some hosts surface REST API rules under “Advanced” or “Performance” tabs. If you don’t see anything, open a support ticket—most will raise the limit or whitelist your integration’s IP within a few hours.

    Security plugins: look for REST API settings buried in advanced tabs

    Wordfence, iThemes Security, and similar plugins can block or throttle REST requests as part of brute-force protection. The setting is rarely front-and-centre.

    In Wordfence, navigate to Firewall → All Firewall Options → Rate Limiting. You’ll see separate rules for login attempts, 404s, and—sometimes—REST API calls. The default may be as low as 10 requests per minute from a single IP.

    If your automation or app uses a fixed IP, whitelist it under Tools → Whitelisted IPs rather than disabling the throttle entirely. If the IP rotates (common with cloud functions or SaaS tools), you’ll need to raise the per-minute cap or exempt the specific endpoint your integration calls.

    iThemes Security groups REST API controls under Settings → Advanced → REST API. You can disable access entirely, restrict to authenticated users, or allow open access. There’s no granular per-endpoint throttle in the free version.

    When to raise limits vs. refactor your integration

    Raising a rate limit is the right move when you control the integration and know the request volume is legitimate—publishing workflows, syncing subscriber counts, or feeding a mobile app.

    But if you’re polling every few seconds because you don’t have webhook support, the real fix is architectural. WordPress supports action hooks that fire when content changes. Use those to push updates to your external service instead of pulling on a loop.

    For example, if you’re auto-posting new articles to social media, hook into publish_post and send the data once. That replaces a 60-requests-per-hour polling schedule with a single request per publish event.

    Similarly, if you’re syncing email subscriber counts, check whether your ESP offers a webhook that pings your WordPress install when the count changes, rather than polling their API every five minutes.

    How to test which layer is blocking you

    If you’re not sure where the throttle lives, make a series of rapid REST API calls and watch the response headers.

    Use curl or Postman to hit https://yoursite.com/wp-json/wp/v2/posts twenty times in quick succession. Look for:

    • HTTP 429 with a Retry-After header—likely a WAF or hosting firewall
    • HTTP 403 with a Wordfence or security-plugin signature in the body—plugin block
    • Timeout or connection reset—server-level resource limit, not a REST-specific rule

    Check your server error logs (usually under /logs/ in your hosting panel or via SSH at /var/log/apache2/error.log or /var/log/nginx/error.log). Rate-limit blocks often leave a timestamped entry naming the rule or plugin that triggered them.

    If nothing appears in logs and responses are clean until request 61, your host is applying an undocumented per-minute cap. Contact support with the exact timestamp and endpoint—they’ll confirm and adjust if appropriate.

    Running a content site with integrations that depend on the REST API? Subscribe to One Two Three Send for weekly breakdowns of the infrastructure details that trip up solo operators—delivered every Wednesday.

    One non-obvious tip: exempt RSS-to-email tools from REST throttles

    If you use Zapier, Make, or an RSS-to-email service to auto-send new posts to your list, those tools often poll your site’s RSS feed—which some setups route through the REST API via /wp-json/wp/v2/posts?_embed.

    Throttling that endpoint can delay your email send by hours. Whitelist the tool’s IP range or exempt the specific /wp/v2/posts path from rate rules. Most security plugins let you exclude endpoints by regex pattern.

    For Zapier, the current IP ranges are published in their help docs (checked October 2026). Add those to your firewall’s allowlist if you’re seeing delayed triggers.

  • WordPress transactional email: SMTP vs. API and when each fails

    WordPress transactional email: SMTP vs. API and when each fails

    WordPress transactional email: SMTP vs. API and when each fails
    Photo by Philip Oroni on Unsplash

    WordPress sends two kinds of email: the newsletters and marketing messages you schedule through a platform, and the transactional messages your site generates automatically—password resets, comment notifications, form submissions, order confirmations. Most operators notice the latter only when it breaks.

    Transactional email typically leaves your server through one of two methods: SMTP (Simple Mail Transfer Protocol) or API. Each has different failure modes, and mixing them up costs you customer trust and support time.

    SMTP: the older, slower, more fragile path

    SMTP is the internet’s original email-sending protocol, defined in RFC 5321 (checked October 2026). When WordPress uses SMTP, it opens a connection to a mail server, authenticates, hands over the message, and waits for confirmation. Plugins like WP Mail SMTP and Easy WP SMTP reconfigure WordPress to route outbound mail through a dedicated SMTP service instead of your web host’s default mail server.

    SMTP works, but it’s synchronous: WordPress waits for the handshake to complete before moving on. If the remote server is slow or the connection times out, your user sees a spinning loader or an error. Shared hosting providers often throttle or block port 25 (the default SMTP port) to prevent spam, forcing you to use port 587 or 465 with TLS authentication. Firewall rules, IP reputation scores, and greylisting all introduce points of failure.

    When SMTP fails, the symptom is usually silence—no error logged, no email delivered, no bounce notification. WordPress assumes the handoff succeeded because the SMTP server accepted the connection, but downstream filters or reputation blocks kill the message before it reaches the inbox.

    API: faster, more observable, purpose-built for transactional mail

    API-based transactional email services—Postmark, SendGrid, Amazon SES, Mailgun—let WordPress send a JSON payload over HTTPS instead of negotiating an SMTP handshake. The request completes in milliseconds, returns a message ID, and hands off delivery to infrastructure purpose-built for high-volume transactional sending.

    API transport is asynchronous by design. WordPress fires the request and moves on; the service handles queuing, retries, and reputation management. You get webhooks for bounces, complaints, and delivery events, and a dashboard that shows exactly which messages were sent, opened, or blocked. When an API call fails, you see an HTTP status code and an error message—usually authentication errors, rate limits, or malformed payloads.

    The trade-off: API services require a plugin (or custom code) to replace WordPress’s default wp_mail() function. You’re also introducing a dependency on a third-party service. If their API goes down or your authentication token expires, transactional mail stops until you fix it. SMTP plugins can fall back to the host’s mail server; API plugins typically can’t.

    When each method breaks—and how to tell

    SMTP failures are usually silent and network-layer: port blocks, authentication mismatches, IP reputation issues, SPF or DKIM misconfigurations. You’ll see timeouts in server logs, but WordPress itself often reports success. Tools like MXToolbox (checked October 2026) help you test DNS records and mail-server reachability from outside your hosting environment.

    API failures are louder and application-layer: invalid API keys, exceeded rate limits, rejected sender domains, webhook signature mismatches. Check your plugin’s debug log and the service’s activity dashboard. Most API providers offer a test-send endpoint you can call from the plugin settings to verify the integration before a customer triggers a real message.

    For high-stakes transactional email—password resets, two-factor codes, order confirmations—API transport is the safer default. Postmark, for example, focuses exclusively on transactional delivery and publishes a real-time deliverability dashboard (checked October 2026). Services that blend marketing and transactional sending (SendGrid, Mailgun) require careful IP-pool and subdomain configuration to keep reputation clean.

    Pick the method that matches your failure budget

    If your site sends fewer than a hundred transactional emails a month and you’re comfortable diagnosing SMTP authentication in cPanel, a basic SMTP plugin works. If missed password resets cost you support hours or revenue, route transactional mail through a dedicated API service and set up a separate monitoring ping for delivery rate.

    Don’t rely on your web host’s default mail server for anything that matters. Shared hosting mail has no reputation management, no bounce handling, and no visibility. The five dollars a month you save by skipping a dedicated transactional service costs you more the first time a customer’s order confirmation lands in spam.

    Want more infrastructure deep-dives like this? Subscribe to One Two Three Send and get one operator-focused article every morning.

    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 database table indexes: where queries slow down and how to check

    WordPress database table indexes: where queries slow down and how to check

    WordPress database table indexes: where queries slow down and how to check
    Photo by Rahul Mishra on Unsplash

    Your WordPress site feels slow. You’ve already compressed images, minified CSS, enabled object caching—and page load times are still terrible. The culprit is often hiding in plain sight: database table indexes that are missing, bloated, or broken.

    Most solo operators never look at their database structure until something breaks. But queries that scan hundreds of thousands of rows without an index can add seconds to page generation time, even on small sites. Here’s how to find the problem tables and decide what to do about them.

    What indexes do and why they break

    An index is a sorted lookup table that helps MySQL find rows without scanning the entire table. When WordPress runs a query like SELECT * FROM wp_posts WHERE post_status = 'publish' ORDER BY post_date DESC, MySQL checks for an index on post_status and post_date. If those indexes exist, the query returns in milliseconds. Without them, MySQL reads every row in the table—thousands or millions of disk operations.

    Indexes break or go missing for a few reasons:

    • Plugin migrations that create custom tables without proper indexes
    • Large DELETE operations that leave fragmented indexes behind
    • Manual SQL imports that skip index definitions
    • Composite indexes that don’t match the query’s WHERE clauses

    The WordPress core tables—wp_posts, wp_postmeta, wp_options, wp_comments—ship with solid default indexes (WordPress Codex database schema reference, checked October 2026). But plugins that add metadata, log tables, or custom post types often don’t add indexes for their own queries.

    How to identify slow queries and missing indexes

    Start by enabling MySQL’s slow query log. Most managed WordPress hosts (including BigScoots) expose this in their dashboard or phpMyAdmin. If you run your own server, add these lines to my.cnf or my.ini:

    slow_query_log = 1
    slow_query_log_file = /var/log/mysql/slow-query.log
    long_query_time = 1

    Any query taking longer than one second will be logged. Restart MySQL, let your site run under normal traffic for an hour, then open the log. Look for queries with high Rows_examined counts—anything above 10,000 rows for a single SELECT is a red flag.

    Next, check which tables are missing indexes. Log into phpMyAdmin or your database client, select your WordPress database, and run:

    SHOW TABLE STATUS;

    Look at the Data_length and Index_length columns. If a table has 50 MB of data but only 2 MB of indexes, and you know plugins are querying it heavily, you likely need to add indexes.

    To see which indexes exist on a specific table, run:

    SHOW INDEX FROM wp_postmeta;

    WordPress adds a post_id index and a meta_key index by default. If a plugin queries WHERE meta_key = 'custom_field' AND meta_value = 'something', and there’s no composite index on (meta_key, meta_value), that query will be slow.

    When to add an index and when to leave it alone

    Adding an index speeds up reads but slows down writes. Every INSERT, UPDATE, or DELETE has to rebuild the affected indexes. For tables that change constantly—like wp_options with autoloaded data—a poorly chosen index can make things worse.

    Add an index if:

    • The slow query log shows repeated queries scanning tens of thousands of rows
    • The table is read-heavy (e.g., custom post type archives, metadata lookups)
    • The query filters or sorts on a column that isn’t indexed

    Skip the index if:

    • The table has fewer than 1,000 rows (the overhead isn’t worth it)
    • The column has very low cardinality (e.g., a boolean field with only two values)
    • Write performance matters more than read performance (e.g., logging tables)

    To add a single-column index:

    ALTER TABLE wp_postmeta ADD INDEX meta_value_idx (meta_value(191));

    The (191) limits the indexed portion of the column to 191 characters, which keeps the index size manageable for utf8mb4 collation (MySQL CREATE INDEX documentation, checked October 2026).

    For composite indexes matching a specific query pattern:

    ALTER TABLE wp_postmeta ADD INDEX meta_key_value_idx (meta_key, meta_value(191));

    The non-obvious tip: rebuild fragmented indexes quarterly

    Even well-indexed tables degrade over time. If you run a membership site, a course platform, or any WordPress install with heavy comment or metadata churn, indexes become fragmented. MySQL has to read more disk blocks to traverse the index tree, and queries slow down even though the index exists.

    Check fragmentation by running:

    SHOW TABLE STATUS LIKE 'wp_postmeta';

    Look at the Data_free column. If it’s more than 20% of Data_length, the table is fragmented. Rebuild it with:

    OPTIMIZE TABLE wp_postmeta;

    This locks the table briefly (usually seconds for tables under 1 GB), so run it during low-traffic windows. Set a calendar reminder to OPTIMIZE your largest tables every quarter. On a site with 50,000 posts and heavy metadata, this alone can cut query times in half.

    Want more infrastructure deep-dives like this? Subscribe to One Two Three Send—every article is written for operators who want to understand the layer below the dashboard.

  • WordPress comment moderation queues: how filters decide what lands where

    WordPress comment moderation queues: how filters decide what lands where

    WordPress comment moderation queues: how filters decide what lands where
    Photo: Piotrus via Wikimedia Commons (CC0)

    WordPress comment moderation is a black box until you dig into the settings. A first-time visitor leaves a thoughtful reply and it vanishes into the moderation queue. A known commenter posts a link and gets flagged as spam. The system uses a set of filtering rules—some visible, some buried—that decide whether a comment goes live, waits for approval, or gets junked.

    Understanding how these filters work lets you tune moderation to match your traffic pattern. If you run a low-volume blog, you can loosen the gate. If you attract drive-by spam, you tighten it. The defaults are conservative, built for a web that doesn’t exist anymore.

    The approval workflow: three possible destinations

    Every comment WordPress receives lands in one of three places: approved and published immediately, held in the moderation queue, or sent to spam (or trash, if you use an external filter like Akismet). The path depends on a chain of checks that run in sequence.

    First, WordPress checks whether the commenter has a previously approved comment using the same email address. If yes, and if the “Comment author must have a previously approved comment” box is checked under Settings → Discussion, the new comment publishes immediately. This is the fast lane for repeat commenters. If the box is unchecked, every comment waits for manual approval regardless of history.

    Next, WordPress scans the comment body, author name, email, URL, and IP address against two lists: the moderation keyword list and the disallowed keyword list. The moderation list (labelled “Comment Moderation” in the dashboard) holds comments for review. The disallowed list (formerly called the blacklist, relabelled in WordPress 5.5) sends comments straight to spam. Both lists live under Settings → Discussion and accept one keyword or phrase per line.

    Finally, WordPress counts links. By default, if a comment contains two or more links, it goes to moderation. You can change that threshold in the “Hold a comment in the queue if it contains [X] or more links” field. Set it to zero and every comment with any link gets held. Set it to ten and you let through link-heavy replies that are probably spam.

    Keyword matching: case-insensitive and substring-aware

    The keyword filters are simple but powerful. WordPress treats every entry as case-insensitive and matches substrings. If you add “casino” to the disallowed list, it will catch “casino,” “Casino,” and “online-casino-bonuses.” It will also catch “occasion” and “Casiano”—any string containing those letters in sequence.

    This substring behaviour causes false positives. A moderation list that includes “free” will flag “freedom,” “freelance,” and “gluten-free.” You can work around this by adding spaces before and after the keyword (” free “), which narrows the match to whole words surrounded by whitespace. It’s not perfect—it won’t catch “free!” or “free.”—but it reduces collateral damage.

    The moderation and disallowed lists work independently. A comment caught by the disallowed list never reaches the moderation queue; it skips straight to spam. A comment caught by the moderation list waits for manual review, even if the author has prior approved comments.

    IP and email history: where external plugins step in

    WordPress core doesn’t maintain a reputation database for IP addresses or email domains beyond the simple “previously approved” check. It doesn’t track how many times an IP has been flagged, or whether an email domain is a known spam source. For that, you need a plugin.

    Akismet, bundled with most WordPress installs, sends every comment to its cloud service for scoring. It checks the comment content, author metadata, and submission context against a global spam database and returns a spam/not-spam verdict. Akismet’s accuracy is high, but it’s a third-party dependency with a commercial pricing tier (checked October 2026) for business sites. Free usage is limited to personal blogs.

    Antispam Bee, a privacy-focused alternative, runs locally and uses pattern matching, honeypot fields, and country-based IP filtering. It doesn’t phone home, but it also doesn’t benefit from network-effect spam detection. Which tool fits depends on your privacy stance and comment volume.

    When to adjust the defaults

    If you’re launching a new site with zero comment history, uncheck “Comment author must have a previously approved comment.” Otherwise, every first-time commenter—including engaged readers—waits in the queue, and you create friction where you want conversation.

    If your niche attracts keyword-heavy spam (crypto, pharma, affiliate), populate the disallowed list early. Start with obvious terms and expand as patterns emerge. But review the spam folder weekly; automated filters drift and catch legitimate comments over time.

    If you cross-post to social media and drive external traffic, raise the link threshold to three or four. Readers who quote sources or share related articles shouldn’t wait for approval. If spam is minimal, you can disable link-count moderation entirely by leaving the field blank or setting it to a high number.

    The comment system in WordPress hasn’t changed much in a decade, but the web around it has. Spam is more sophisticated, and legitimate commenters expect instant feedback. Tuning the moderation filters to match your traffic pattern is a small lever that improves the reader experience without adding workload.

    Want more infrastructure walkthroughs like this? Subscribe to One Two Three Send—we break down the mechanics of online-business tools every week, no fluff.

  • WordPress auto-save intervals: how often drafts write and when you lose work

    WordPress auto-save intervals: how often drafts write and when you lose work

    WordPress auto-save intervals: how often drafts write and when you lose work
    Photo by Markus Winkler on Unsplash

    WordPress auto-save is supposed to be invisible. You type, it saves, and you never think about it—until you refresh a draft and realise the last four paragraphs are gone.

    The default auto-save interval is 60 seconds. That’s a full minute of typing that can vanish if your browser crashes, your session times out, or a plugin interferes with the AJAX request that writes the draft to the database.

    Most solo operators don’t realise how often WordPress doesn’t save, or what conditions break the cycle. Here’s what actually happens, and how to adjust it if you’re losing work.

    How WordPress auto-save works

    WordPress fires an AJAX request to wp-admin/admin-ajax.php every 60 seconds while you’re editing a post or page. That request writes a revision to the wp_posts table with post_status = 'auto-draft'.

    If the request succeeds, you’ll see a faint “Draft saved at [time]” notice near the top of the editor. If it fails—because your session expired, your network dropped, or a security plugin blocked the request—you’ll see nothing. WordPress assumes the save worked.

    The problem: WordPress doesn’t retry failed auto-saves. If the AJAX call times out or returns a 403, the next save attempt happens 60 seconds later. Anything you typed in between is at risk.

    This is especially common on shared hosting, where PHP execution time limits, memory caps, or aggressive WAF rules can silently kill the auto-save request. You’ll only notice when you reload the page or lose focus and see an older version of your draft.

    When auto-save fails silently

    Four scenarios break auto-save more often than operators expect:

    • Multiple tabs editing the same post. WordPress locks posts to a single session, but if you open the same draft in two browser tabs, the second tab’s auto-save requests return 409 conflicts. The editor doesn’t warn you—it just stops saving.
    • Session timeout during long edits. Default WordPress sessions expire after 48 hours, but hosting providers often override this to 2–12 hours. If your auth cookie expires mid-edit, auto-save returns 401 and stops. You’ll only notice when you hit Publish and get bounced to the login screen.
    • Security plugins blocking AJAX. Wordfence, Sucuri, and iThemes Security all have rules that can block rapid-fire AJAX requests from the same IP. If you’re editing quickly, the plugin may flag auto-save as suspicious and return 403. Check your security log—most operators never look.
    • Browser background-tab throttling. Chrome, Safari, and Edge all throttle JavaScript timers in background tabs to save battery. If you’re editing a post in one tab while reading documentation in another, the auto-save interval can stretch to 2–5 minutes instead of 60 seconds.

    How to shorten the auto-save interval

    WordPress lets you override the 60-second default with a single line in wp-config.php:

    define( 'AUTOSAVE_INTERVAL', 30 );

    That changes the interval to 30 seconds. You can set it as low as 10 seconds, but be aware: shorter intervals increase database writes and server load. If you’re on shared hosting with tight I/O limits, aggressive auto-save can slow down the editor or trigger rate-limit warnings from your host.

    A better approach for most solo operators: leave the interval at 60 seconds, but add client-side protection with a browser extension like Textarea Cache (Chrome) or Lazarus (Firefox). These tools store form content in local browser storage every few seconds, independent of WordPress. If the page crashes or the session expires, you can recover the text from the extension’s cache.

    What gets saved and what doesn’t

    WordPress auto-save only writes the post title, content, and excerpt. It does not save:

    • Custom field values (ACF, Pods, or manual meta fields)
    • Featured image selections
    • Category or tag assignments
    • Gutenberg block settings (only the block HTML is saved, not inspector-panel metadata)

    If you’re using a page builder like Elementor or Bricks, auto-save behaviour is entirely different—they maintain their own revision systems and save intervals. Check the plugin’s settings; most default to 60–120 seconds and store data in custom database tables, not wp_posts.

    When to disable auto-save

    Some operators disable auto-save entirely to reduce database bloat, especially on sites with hundreds of posts and thousands of revisions. You can turn it off with:

    define( 'AUTOSAVE_INTERVAL', 86400 );

    That sets the interval to 24 hours—effectively disabling it. But unless you’re manually saving every few minutes with Cmd+S or Ctrl+S, this is risky. A single browser crash can lose an hour of work.

    A safer middle ground: leave auto-save enabled, but limit the number of revisions WordPress stores. Add this to wp-config.php:

    define( 'WP_POST_REVISIONS', 5 );

    That keeps only the five most recent revisions per post. It prevents database bloat without removing the safety net.

    Reply and let us know: Have you ever lost a draft to a failed auto-save? What interval do you run, and have you adjusted it? Hit reply—we read every response.