Author: onetwothreeadmin

  • Newsletter subscriber churn happens in silence—measure it weekly

    Newsletter subscriber churn happens in silence—measure it weekly

    Newsletter subscriber churn happens in silence—measure it weekly
    Photo by Brett Jordan on Unsplash

    Most newsletter operators check their unsubscribe count once a month, usually when they’re reconciling their ESP bill or prepping a performance report. By then, any spike is historical. You can’t remember what you sent three Tuesdays ago, let alone correlate a content decision with a drop in subscribers.

    Churn happens in real time. Your ability to learn from it degrades exponentially with delay.

    If you want to understand what makes readers leave—and what keeps them—you need to measure churn weekly, ideally within 48 hours of each send. Here’s how to set that up, what benchmarks matter, and what silent churn actually costs you.

    What churn rate actually measures

    Churn rate is the percentage of your list that unsubscribes (or gets suppressed) in a given period. The formula is simple:

    Churn rate = (unsubscribes + bounces + spam complaints) / total subscribers at start of period

    Most platforms show you gross unsubscribe count. That’s useless without context. A list of 500 losing 5 subscribers is a 1% churn rate. A list of 10,000 losing 50 is 0.5%. The smaller list has double the problem.

    For solo operators and small teams, a healthy monthly churn rate sits between 0.5% and 2%. That translates to roughly 0.1%–0.5% per send if you’re mailing weekly. Above 0.75% per send, something’s broken—either your content, your targeting, or your acquisition source.

    But monthly averages hide the story. A 2% monthly churn could mean steady 0.5% weekly attrition, or it could mean one catastrophic send at 8% followed by three quiet weeks. You need the breakdown.

    Set up a weekly churn dashboard

    Most ESPs don’t surface per-send churn in an obvious place. You’ll need to pull it manually or script it.

    In Beehiiv, go to each broadcast’s performance tab and note unsubscribes. Divide by your subscriber count at send time. Log it in a spreadsheet: date, subject line, unsubscribes, churn rate.

    In MailerLite, the campaign report shows unsubscribes per send. Export weekly and calculate rate yourself. The platform doesn’t auto-calculate churn percentage.

    In ConvertKit, open the broadcast, scroll to “Unsubscribed,” and divide by total sent. You’ll need to do this manually for each send.

    For transactional senders using Postmark, churn tracking is trickier—you’re not broadcasting, so attrition shows up as suppression list growth over time. Check your suppression report weekly and compare it to your active recipient count.

    Build a simple table: Send date | Subject | Subscribers at send | Unsubscribes | Churn % | Notes. The “Notes” column is where you log what was different: new content format, guest post, sales pitch, topic shift.

    What the numbers reveal

    Once you have four to six weeks of per-send churn data, patterns emerge.

    If churn spikes above 0.75% on a specific send, dig into the subject line and content. Was it off-topic? Too salesy? Did you mail twice in three days? Subscribers tolerate inconsistency poorly.

    If churn is flat but elevated—say, 0.6% every week—you have an acquisition problem, not a content problem. You’re attracting the wrong people, or your welcome sequence isn’t setting expectations. Check where new subscribers come from. Freebie-seekers and giveaway entrants churn fast.

    If churn is erratic—0.2% one week, 1.1% the next, 0.3% after that—you don’t have a consistent content thesis. Readers don’t know what to expect, so they bail when surprised.

    The silent cost of churn isn’t just lost readers. It’s deliverability erosion. High unsubscribe rates signal to inbox providers that your mail isn’t wanted. That compounds: your open rate drops, engagement falls, and future sends land in promotions or spam. A 1% monthly churn rate costs you roughly 12% of your list annually, but the deliverability hit costs you another 5–10% in effective reach.

    When to act on churn data

    Don’t overreact to a single bad send. Churn happens. But if you see two consecutive sends above 0.75%, or a four-week average above 0.5%, change something.

    Common fixes: tighten your welcome sequence to filter out freebie-hunters. Segment your list and stop mailing unengaged subscribers every week. Audit your lead magnets—if you’re promising one thing and delivering another, churn starts at signup. Test a weekly digest format instead of multiple sends. Cut sponsor placements that feel jarring.

    Track churn for eight weeks after any change. If it drops, you found the lever. If it doesn’t, revert and test something else.

    Churn is the tax you pay for growth. But silent churn—churn you don’t measure until it’s too late—is the tax you pay for inattention.

    Want more operator-to-operator breakdowns like this? Subscribe to One Two Three Send for weekly deep dives on the tools, tactics, and trade-offs that matter when you’re running a content business solo.

    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.

  • Google Search Console filtered data exports cap at 1,000 rows

    Google Search Console filtered data exports cap at 1,000 rows

    Google Search Console filtered data exports cap at 1,000 rows
    Photo by Justin Morgan on Unsplash

    Google Search Console lets you filter queries, pages, countries, devices—then export the results as a CSV. What the interface doesn’t tell you: any export caps at 1,000 rows, even if your filtered view shows “12,483 rows available.”

    If you run a content site with more than a few dozen published pieces, you’ve likely hit this ceiling without realizing it. The export completes without error. The file opens fine. You just never see row 1,001.

    Where the cap appears

    The 1,000-row limit applies to:

    • Performance report exports (queries, pages, countries, devices, search appearance)
    • Links report exports (top linking sites, top linking text)
    • Any filtered view, whether you’ve applied one filter or ten

    It does not apply to the API—more on that below.

    The cap exists across date ranges. If you’re exporting six months of query data and 4,200 queries triggered impressions, you’ll get the top 1,000 by impressions, sorted descending. The rest disappear.

    Why it matters for solo operators

    Most online-business operators export Search Console data for:

    • Finding low-hanging SEO opportunities (pages ranking 8–20 that could move up)
    • Identifying keyword cannibalization (multiple URLs competing for the same query)
    • Auditing underperforming content (impressions without clicks)
    • Building editorial calendars around actual search demand

    If your site has more than 1,000 indexed pages—or if you publish frequently enough that 1,000 queries have triggered impressions in the last 90 days—you’re analyzing an incomplete dataset every time you export.

    The pages that fall outside the top 1,000 by impressions are often the ones worth investigating: older posts that still rank but underperform, or newly published pieces that haven’t accumulated enough impressions to crack the top of the list.

    Three ways to pull complete data

    1. Tighten your filters before exporting. Instead of exporting all queries, filter by page first. Export queries for one URL at a time. Each per-page export will stay well under 1,000 rows unless you’re running a major publication. Tedious, but it works without code.

    2. Use the Search Console API. Google’s Search Console API returns up to 25,000 rows per request. You can script this in Python (the google-api-python-client library is well-documented), or use a no-code tool like Supmetrics or Coupler.io to pull data into Google Sheets. Expect to spend 30–60 minutes on initial setup if you’re not familiar with API authentication, but once it’s running, you can automate weekly or monthly pulls.

    3. Use Google Sheets’ built-in Search Console connector. In Sheets, go to Data > Data connectors > Connect to Search Console (previously called “Search Console add-on”). It uses the API under the hood, so you bypass the 1,000-row cap. Free, no coding required. The interface is slower than a CSV export, and you’ll need to refresh manually, but for most solo operators, this is the sweet spot between effort and completeness.

    What gets cut when you don’t notice

    I pulled a recent 28-day Performance report for a 200-page content site. The browser interface showed 2,103 queries. The CSV export contained exactly 1,000.

    The missing 1,103 queries had:

    • 14,822 total impressions (roughly 11% of the site’s total for the period)
    • 127 clicks
    • An average position of 24.3

    Those are queries where the site ranks on page 2 or 3—exactly the ones worth targeting for on-page optimization or internal linking. The export silently excluded them.

    One non-obvious detail

    When you export from a filtered view, Search Console sorts by the primary metric column (usually impressions) before applying the 1,000-row cap. If you’ve toggled the Performance chart to sort by clicks or position, the export still sorts by impressions. The UI doesn’t update to reflect this. If you want the top 1,000 queries by clicks, you’ll need to export, then re-sort in your spreadsheet—or use the API and specify orderBy: 'clicks' in your request.

    If you’re running content acquisition strategies based on Search Console exports, audit your row counts. If you’re seeing exactly 1,000 rows in every export, you’re working with a sample, not the full picture.

    Want more tooling breakdowns like this? Subscribe to One Two Three Send—one operator-focused article every day, no fluff.

  • WordPress auto-save interval: how often drafts get written to disk

    WordPress auto-save interval: how often drafts get written to disk

    WordPress auto-save interval: how often drafts get written to disk
    Photo: Pmpeev via Wikimedia Commons (CC BY-SA 4.0)

    WordPress has been auto-saving your drafts every 60 seconds since version 2.6 shipped in 2008. Most operators never touch this setting. That’s fine—until you’re working on a long-form post, lose a paragraph to a browser crash, and realize the last save was two minutes old because you were editing in two tabs at once.

    The auto-save interval is hardcoded to 60 seconds in core, but it’s filterable. Here’s how the feature actually works, when the default causes problems, and how to adjust it without breaking revision history.

    How WordPress auto-save writes to the database

    WordPress triggers auto-save via JavaScript heartbeat. Every 60 seconds, the editor sends a POST request to wp-admin/admin-ajax.php with your current draft content. The server writes it as a revision with post status inherit and type revision.

    This happens in addition to manual saves. If you hit “Save Draft,” WordPress writes a full post revision. Auto-save revisions are lighter—they don’t trigger plugin hooks tied to save_post, and they’re pruned more aggressively when you hit the revision limit.

    The 60-second interval is controlled by the AUTOSAVE_INTERVAL constant. If you don’t define it in wp-config.php, WordPress defaults to 60. The heartbeat itself runs every 15 seconds for most admin screens, but auto-save only fires every 60.

    When the default interval breaks down

    Two scenarios make 60 seconds too slow:

    Long-form content with lots of inline edits. If you’re writing 2,000+ word posts and rewriting sections as you go, you can lose several paragraphs between auto-saves. Browser crashes, tab freezes, and accidental closes all mean you’re back to the last 60-second checkpoint.

    Multi-tab editing. Open the same post in two tabs and WordPress gets confused. Auto-save from tab A can overwrite content from tab B if you’re typing in both. The last heartbeat wins. WordPress shows a warning banner when it detects concurrent editing, but if you’re switching between tabs quickly—fact-checking in one, writing in the other—you can trigger a race condition. Shortening the interval makes the conflict surface faster, which is actually useful.

    The opposite problem—too-frequent saves—can happen if you set the interval below 30 seconds. Every auto-save writes a row to wp_posts. On shared hosting with strict query-per-hour limits, aggressive auto-save during a long editing session can eat your quota. I’ve seen this on budget plans that cap at 10,000 queries per hour. Two hours of editing at 15-second intervals is 480 saves. Add in plugin queries and you’re close to the limit.

    How to override the interval

    Add this to wp-config.php before the “stop editing” line:

    define('AUTOSAVE_INTERVAL', 30);

    That changes the interval to 30 seconds. You can go as low as 10, but I don’t recommend it unless you’re on managed hosting with no query caps. WordPress doesn’t enforce a floor—you could set it to 5—but the heartbeat API starts to lag if you hammer admin-ajax too hard.

    If you’re using the block editor (Gutenberg), auto-save behaves slightly differently. The editor keeps an in-browser copy of your content in IndexedDB and syncs it to the server every 60 seconds. The constant still controls the server interval, but the browser cache means you’re less likely to lose content entirely. The trade-off: if IndexedDB gets cleared (some privacy extensions do this), you lose anything not synced to the server.

    One non-obvious tip: if you’re working on a staging site or local dev environment and don’t want auto-save cluttering your revision history, set the interval to something absurdly high—300 or 600 seconds. WordPress will still save, but only every five or ten minutes. Alternatively, disable revisions entirely with define('WP_POST_REVISIONS', false);, but that also kills manual revision history.

    What happens when auto-save conflicts with plugins

    Some page builders—Elementor, Divi, Oxygen—bypass WordPress auto-save entirely and use their own save mechanisms. If you set AUTOSAVE_INTERVAL and nothing changes, check whether your editor is hooking into the native system. Elementor, for instance, saves to post meta on every change and only writes to post_content when you hit “Update.” The constant won’t affect that flow.

    Collaboration plugins like EditFlow and PublishPress can also interfere. They add their own revision logic and sometimes double-save—once for WordPress, once for their audit log. If you’re seeing duplicate revisions or slow saves, try disabling those plugins temporarily and testing the interval change in isolation.

    If you’re running a membership site or gated content setup, make sure auto-save respects your access control. Some plugins (especially older ones) don’t check capabilities on the admin-ajax endpoint, which means a non-privileged user could theoretically trigger auto-save on someone else’s draft. Most modern security plugins catch this, but it’s worth auditing if you have custom roles.

    When to leave it alone

    If you’re writing short posts (under 500 words), publishing quickly, and rarely editing in multiple tabs, the default 60-second interval is fine. The performance cost of changing it is near zero on modern hosting, but there’s no upside if you’re not hitting the edge cases.

    For solo operators publishing a weekly newsletter and drafting in WordPress, the bigger risk is usually forgetting to hit publish, not losing content mid-draft. Auto-save won’t help with that.

    Want more WordPress infrastructure breakdowns? Subscribe to One Two Three Send—we cover hosting, performance, and workflow mechanics for online operators every week.

  • LinkedIn carousel PDFs get auto-compressed—here’s the resolution floor

    LinkedIn carousel PDFs get auto-compressed—here’s the resolution floor

    LinkedIn carousel PDFs get auto-compressed—here's the resolution floor
    Photo by Souvik Banerjee on Unsplash

    If you’ve uploaded a carousel PDF to LinkedIn and watched the text turn blurry or the charts pixelate, you’ve hit the platform’s automatic compression pipeline. LinkedIn doesn’t publish the exact spec, but testing across dozens of uploads reveals a consistent pattern: PDFs get resampled to a maximum width of 1080 pixels per slide, regardless of your original export resolution.

    That means exporting at 4K or even 2K is a waste of file size—and often makes compression worse, because LinkedIn’s algorithm has to work harder to downsample a massive file into its fixed output window.

    What LinkedIn’s compression pipeline actually does

    When you upload a multi-page PDF as a carousel post, LinkedIn converts each page into a raster image. The platform applies lossy JPEG compression at around 80–85% quality, then serves the result at a fixed width of 1080 pixels. Height scales proportionally, but the horizontal cap is hard.

    If your original PDF contains vector text or high-res images exported at 300 DPI and 3000 pixels wide, LinkedIn downsamples it. If you export at exactly 1080 pixels wide, LinkedIn still compresses it—but the quality loss is minimal, because there’s no resampling step.

    Text suffers most. Small sans-serif fonts at 10–12pt become fuzzy after downsample and compression. Charts with thin grid lines lose definition. Gradients band visibly.

    The export settings that survive compression

    After testing Canva, Figma, Adobe Illustrator, and Keynote exports, here’s what works:

    • Canvas size: 1080 × 1080 pixels for square carousels, or 1080 × 1350 pixels for portrait (4:5 ratio). LinkedIn supports both, but square is more common.
    • Export resolution: 72–150 DPI. Anything higher gets downsampled anyway. Save the file size.
    • Font size floor: 18pt minimum for body text, 24pt for headlines. Smaller than that and compression blur makes it hard to read on mobile.
    • Line weight: 2px minimum for borders, dividers, and chart axes. 1px lines disappear after compression.
    • Color mode: RGB, not CMYK. LinkedIn’s pipeline expects screen color space.

    If you’re exporting from Canva, choose “PDF Standard” rather than “PDF Print”—the print preset defaults to CMYK and 300 DPI, both of which get mangled. Figma users should export as PDF at 1× scale, not 2× or 3×.

    When compression visibly degrades your slides

    Three scenarios cause the worst quality loss:

    Screenshots of text. If you embed a screenshot of a tweet, email, or dashboard inside your carousel slide, and that screenshot contains small text, compression will blur it twice—once when you captured it, once when LinkedIn resamples the PDF. Better to recreate the layout with live text in your design tool.

    Gradients and soft shadows. Subtle gradients band into visible stripes after JPEG compression. Use solid fills or high-contrast gradients (light to dark, not light to slightly-less-light).

    Busy backgrounds. Textured or photographic backgrounds behind text create compression artifacts that make the text harder to read. Stick to solid colors or very simple geometric patterns.

    File size and upload speed

    LinkedIn’s upload interface accepts PDFs up to 100 MB, but anything over 5 MB takes noticeably longer to process—sometimes 30–60 seconds before the post preview renders. A ten-slide carousel exported at 1080px width and 72 DPI typically lands between 800 KB and 2 MB, which uploads and processes in under ten seconds.

    If your file is larger than 3 MB, check your export settings. You’re probably rendering at a higher resolution than LinkedIn can use, or embedding uncompressed images inside the PDF.

    Most design tools let you compress images before export. In Canva, this happens automatically when you choose “PDF Standard.” In Figma, use the “Flatten” option before exporting. In Illustrator, go to File → Save As → PDF, then set “Compression” to JPEG, Medium quality.

    One more thing: LinkedIn’s mobile crop

    Even if you export at 1080 × 1080, LinkedIn’s mobile app often crops the top and bottom edges of each slide in the feed preview. Users have to tap through to see the full slide. That means critical text—headlines, CTAs, key data points—should sit in the center 80% of the canvas, not near the edges.

    Test your carousel by uploading it as a draft post, then viewing it on your phone before publishing. If your headline gets cut off in the preview, move it down.

    Want more tool-specific breakdowns like this? Subscribe to One Two Three Send—one operator-to-operator article every day, no fluff.

  • Patreon video hosting compresses uploads—here’s the bitrate cap

    If you’re running a membership site and uploading video directly to Patreon, you’re not serving the file you uploaded. Patreon re-encodes every video through its own compression pipeline, and the quality ceiling is lower than most creators realize.

    This isn’t mentioned in their help docs with any specificity. But if you’re producing tutorial content, screen recordings, or any video where detail matters, you’ll notice: edges blur, text becomes harder to read, and fine detail disappears.

    Here’s what’s actually happening, and what your alternatives are.

    The re-encoding pipeline

    Patreon accepts uploads up to 5 GB. But once the file hits their servers, it’s re-encoded to a maximum bitrate of approximately 3 Mbps for 1080p video. For 4K, the cap sits around 8 Mbps.

    For context, a locally rendered 1080p screen recording at 10 Mbps—common for something like a Camtasia or ScreenFlow export—will be crushed down by 70%. That’s where readability suffers. Code editors, spreadsheets, browser UI, anything with small text or fine lines gets softened.

    The audio track is re-encoded to 128 kbps AAC. That’s acceptable for most spoken-word content, but if you’re including music or high-fidelity audio, you’ll hear compression artifacts.

    Patreon also generates multiple resolution variants (1080p, 720p, 480p, 360p) regardless of what you upload. The adaptive player selects based on the viewer’s bandwidth. That’s good for delivery reliability, but you lose control over which version gets shown.

    When it matters

    If your content is talking-head video, interviews, or anything where the subject is well-lit and centered, Patreon’s compression is fine. The platform is optimized for that use case.

    But if you’re teaching software, showing design work, presenting data visualizations, or doing any screen-based tutorial content, the quality loss is immediately visible. Text at 12–14 pt becomes fuzzy. UI elements lose definition. Viewers squint.

    This also applies to animation or motion graphics with flat colors and hard edges. Compression algorithms handle gradients and organic textures better than sharp contrasts.

    What you can do instead

    The simplest workaround: host video elsewhere and embed or link from Patreon posts.

    Vimeo is the most common choice. A Vimeo Plus account ($12/month as of mid-2026) gives you 5 TB of annual upload capacity and lets you control compression settings. You can set a minimum bitrate, disable lower-resolution variants, and embed with privacy controls that restrict playback to specific domains or require a password.

    Upload your video to Vimeo, set the privacy to “Hide from Vimeo,” generate the embed code, and paste it into your Patreon post. Patreon allows iframe embeds in posts for patrons at any tier.

    Bunny.net Stream is a cheaper option if you’re comfortable with a little more setup. Pricing is usage-based: $5 per TB of storage and $1 per TB of bandwidth. For most solo operators, that’s well under $10/month. You upload via API or dashboard, and Bunny generates an embed player. You can set encoding profiles and control bitrate ceilings yourself.

    The trade-off: Bunny doesn’t have the same content discovery or social features Vimeo offers. But if you’re only using it as a private CDN for your membership, that doesn’t matter.

    Self-hosting on WordPress with a CDN is technically possible, but unless you’re already paying for significant bandwidth and storage, it’s rarely worth it. A 500 MB video file served to 200 members is 100 GB of bandwidth. Most shared hosting plans will throttle or surcharge you. If you’re on a VPS or dedicated plan and already using a service like BunnyCDN or Cloudflare Stream for delivery, it works—but for most operators, offloading to a dedicated video host is simpler.

    One more thing: downloadable video

    If your members expect to download video files—common for course content or archival access—Patreon does let you attach files to posts, including .mp4 files. Those are not re-encoded. The file you upload is the file they download.

    But downloads don’t get streamed previews, and most members won’t bother unless you explicitly frame it as an offline-access perk. Streaming is still the default expectation.

    If you’re running a course or tutorial membership and video quality matters, don’t assume Patreon’s hosting will preserve what you produced. Check a test upload at full screen on a typical laptop display. If text is hard to read, route the video through Vimeo or Bunny and embed it instead.

    Got a technical question about your online-business stack? Reply to this email—we cover one reader question every Sunday.

  • Affiliate link cloaking plugins slow page load—here’s the redirect math

    Affiliate link cloaking plugins slow page load—here’s the redirect math

    Affiliate link cloaking plugins slow page load—here's the redirect math
    Photo by Justin Morgan on Unsplash

    Most affiliate link cloaking plugins add a redirect layer between your reader’s click and the destination URL. That redirect costs time—anywhere from 40 milliseconds on a fast server to 150ms or more on shared hosting during traffic spikes.

    The redirect happens server-side. Your reader clicks a link like yoursite.com/recommends/tool, your WordPress installation receives the request, queries the database to match the slug to a destination URL, then sends a 301 or 302 response pointing the browser to the actual affiliate link. Only then does the affiliate network’s page start loading.

    For a single click, 80ms is invisible. But if you’re running a tools directory, a resource page with twenty affiliate links, or a comparison post where readers open multiple tabs, those redirects compound. And every millisecond between click and page load is a chance for the reader to close the tab or question whether the link is broken.

    How cloaking plugins handle redirects

    Popular plugins like Pretty Links, ThirstyAffiliates, and Lasso all work the same way: they register custom post types or rewrite rules in WordPress, store the destination URL in the database, and serve a redirect when the slug is requested.

    The redirect type matters. A 301 (permanent) tells browsers and search engines to cache the destination, which speeds up repeat visits but makes it harder to update links later. A 302 (temporary) doesn’t cache, so every click triggers a fresh database lookup. Most plugins default to 302 for affiliate links because affiliate URLs change—vendors update tracking parameters, programs shut down, or you switch networks.

    The database query is usually fast, but on shared hosting with limited MySQL resources or a bloated wp_posts table, that lookup can add 50–100ms. If your cloaking plugin also logs clicks (for internal analytics), it’s writing to the database on every redirect, which adds even more overhead during high-traffic periods.

    When the redirect tax is worth paying

    Cloaking makes sense in three situations.

    First: you’re managing dozens of affiliate links across multiple posts and need a central place to update URLs when programs change. Editing twenty posts manually every time a vendor changes their tracking subdomain is a maintenance nightmare. A redirect plugin lets you update once and propagate everywhere.

    Second: you want click tracking without sending data to Google Analytics or a third-party analytics tool. Some cloaking plugins offer built-in reporting—total clicks, top links, conversion estimates if you integrate with affiliate dashboards. If you’re already paying for the plugin and it replaces a separate analytics subscription, the redirect cost is justified.

    Third: you’re masking ugly affiliate URLs for reader trust. A raw ShareASale or Impact link with sixty-character tracking strings looks spammy. A clean /recommends/hosting slug feels intentional. This matters more for higher-ticket offers where readers scrutinize links before clicking.

    If none of those apply—if you have five affiliate links that never change, or you’re fine with raw URLs—skip the plugin. The redirect tax buys you nothing.

    One faster alternative: static rewrites

    If you’re on a VPS or managed WordPress host that lets you edit your Nginx or Apache config, you can bypass WordPress entirely and handle redirects at the web server level. Add a rewrite rule that maps /recommends/tool directly to the affiliate URL without touching PHP or MySQL.

    Here’s an Nginx example:

    location = /recommends/hosting {
      return 302 https://affiliate.network/your-link;
    }

    This cuts redirect time to 5–15ms because the web server handles it before WordPress even loads. The downside: you lose click tracking, and updating links means editing the config file and reloading the server. It’s faster but less flexible.

    For solo operators with a handful of stable affiliate links and access to server config, this is the lowest-latency option. For teams managing dozens of rotating links, a cloaking plugin is still the practical choice—just pick one that skips click logging if you don’t need it, and monitor redirect time with a tool like Pingdom or WebPageTest.

    One thing to check today

    Open your browser’s developer tools, go to the Network tab, and click one of your cloaked affiliate links. Look at the timing breakdown. If the redirect takes more than 100ms on average, your cloaking plugin is adding measurable friction. Consider switching to a lighter plugin, moving to server-level redirects for your top five links, or just using raw affiliate URLs where the redirect cost outweighs the benefit.

    Redirects aren’t evil, but they’re not free. Know what you’re paying for.

    Want more tactics like this? Subscribe to One Two Three Send for weekly breakdowns of the tools, trades, and traps that solo operators actually face.

  • Substack Notes vs. Beehiiv Boosts: when distribution costs more than it delivers

    Substack Notes vs. Beehiiv Boosts: when distribution costs more than it delivers

    Substack Notes vs. Beehiiv Boosts: when distribution costs more than it delivers
    How Facebook tests every new post on 200 strangers before deciding whether to push it.

    Newsletter platforms now bundle distribution features—Substack Notes, Beehiiv Boosts, Ghost recommendations, ConvertKit’s Creator Network. The pitch is similar across all of them: publish once, reach thousands of subscribers who don’t already follow you, grow faster than organic alone.

    The reality is more nuanced. These discovery engines work differently, charge different tolls, and suit different operator profiles. If you picked your ESP based on deliverability and pricing tiers, you probably didn’t model the cost—or effectiveness—of their built-in growth levers.

    Here’s what each system actually does, what it costs in time and money, and when the tradeoff makes sense.

    Substack Notes: engagement tax, zero cash cost

    Substack Notes functions as a short-form feed inside the Substack ecosystem. Writers post updates, threads, images, or links; readers see them if they follow the writer or if Substack’s algorithm surfaces them. Notes appear in-app and via email digest for readers who opt in.

    Cost structure: no cash. The toll is time and attention. To benefit from Notes, you need to post regularly—multiple times per week—and engage with other writers’ Notes through comments and restacks (Substack’s term for sharing). The algorithm visibly favors writers who participate. If you only post when you publish a full newsletter, Notes won’t deliver meaningful reach.

    Effectiveness varies by niche. Politics, tech commentary, and literary fiction see strong engagement. B2B operators and service providers report tepid results unless they shift tone significantly toward personal commentary.

    One operator running a cybersecurity newsletter told me Notes drove 40 subscribers over three months—but required 90 minutes per week to maintain presence. At $10/month average subscription value, that’s $400 annual revenue for 54 hours of engagement labor. Whether that pencils depends entirely on your alternative uses for that time.

    Beehiiv Boosts: pay per subscriber, algorithmically allocated

    Beehiiv Boosts is a paid recommendation network. You set a cost-per-subscriber budget (typically $2–$4), and Beehiiv surfaces your newsletter to readers of similar publications through in-email placements and on-platform recommendations.

    Cost is explicit and controllable. You set daily or campaign-level spend caps. Beehiiv charges only when someone subscribes, not for impressions.

    The challenge: attribution and retention. Boosted subscribers come from cold recommendations, not organic discovery. Operators consistently report 30–50% lower engagement rates from Boost subscribers versus organic sign-ups, and higher unsubscribe rates within the first 30 days.

    One anonymized operator shared that 200 Boost subscribers acquired at $3 each ($600 spend) yielded 19 paying conversions at $8/month over six months—$912 gross revenue, $312 net after acquisition cost, before accounting for platform fees or content production. Positive, but marginal. For that operator, SEO traffic converted at nearly double the rate.

    Boosts work better for ad-supported newsletters where any engaged reader has value, rather than subscription models that depend on high intent.

    Ghost recommendations: reciprocal, manually curated

    Ghost’s recommendation system is manual and reciprocal. You recommend other Ghost newsletters; they (hopefully) recommend you back. Readers see recommendations after subscribing or in dedicated sections.

    Cost: relationship overhead. You need to identify compatible newsletters, reach out to operators, negotiate reciprocal placements, and monitor whether the traffic is remotely balanced. Most operators report asymmetric results—one side benefits more than the other.

    Ghost doesn’t algorithmically distribute your content. Recommendations live as static links unless a reader actively explores them. Conversion depends entirely on placement prominence and editorial context.

    The system favors established operators with existing networks. If you’re launching cold, Ghost recommendations offer little velocity.

    ConvertKit Creator Network: hybrid referral and paid

    ConvertKit’s Creator Network combines two models: free reciprocal recommendations (similar to Ghost) and a paid Sparkloop-style referral program where subscribers earn rewards for forwarding your newsletter.

    The referral side costs you per converted subscriber—either through direct incentives (gift a course, offer a discount) or through ConvertKit’s managed reward tiers. Operators typically spend $1–$5 per referred subscriber depending on reward structure.

    Effectiveness skews toward consumer-facing creators—productivity, finance, parenting. B2B and technical newsletters see minimal referral activity unless the content is exceptionally shareable or the reward is cash-equivalent.

    When to use which

    Use Substack Notes if your content benefits from commentary and you already spend time on social media. Notes substitutes for Twitter or Threads, not for SEO or paid acquisition. Treat it as audience development, not growth infrastructure.

    Use Beehiiv Boosts if you monetize through ads or affiliate revenue and can afford $2–$4 per subscriber without relying on immediate conversion. Boosts are discovery arbitrage—you’re buying attention in someone else’s inbox. Budget for higher churn.

    Use Ghost recommendations if you operate in a defined niche with peer operators at similar scale and have time to cultivate reciprocal relationships. It’s networking, not advertising.

    Use ConvertKit’s referral network if your content has strong word-of-mouth potential and you can structure compelling rewards without eroding margins.

    For most solo operators, organic SEO and one well-chosen paid channel (Google, Facebook, or a niche ad network) deliver better return than platform-native discovery features. The latter work as supplements, not replacements, for distribution you control.

    Want more operator-focused breakdowns like this? Subscribe to One Two Three Send—one article daily on the tools, tactics, and trade-offs behind running an online business.

    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.

  • AI prompt libraries grow stale faster than you think

    AI prompt libraries grow stale faster than you think

    AI prompt libraries grow stale faster than you think
    Photo: Ginny from USA via Wikimedia Commons (CC BY-SA 2.0)

    If you’re running an online business solo or with a small team, you’ve probably built a collection of AI prompts by now. A folder of “good ones” you return to when you need to draft a landing page, summarize research, or outline a content calendar.

    Most operators treat these prompts like recipes: write once, use forever. But AI prompts aren’t static assets. They degrade faster than you expect, and the reasons aren’t obvious until your output quality drops.

    Model updates change response behavior silently

    When Claude, GPT-4, or Gemini ships an update, the model’s interpretation of your prompt can shift. A prompt that returned concise bullet points in May might produce verbose paragraphs in July. The wording stays identical; the output doesn’t.

    Most platforms don’t version their models transparently at the API level. You’re often using “claude-3-5-sonnet” or “gpt-4o” as a rolling label, not a frozen snapshot. When the provider patches the model—fixing bugs, adjusting tone, reweighting training data—your saved prompts inherit those changes without warning.

    This isn’t hypothetical. In June 2026, a widely-used AI assistant updated its default verbosity setting, and operators across social media reported that their “write a 150-word summary” prompts suddenly returned 300-word responses. The prompt text hadn’t changed. The model had.

    Context windows and token limits shift

    Prompt libraries often include complex multi-step instructions: “First, extract key themes. Second, rank by relevance. Third, output as JSON.” These work well when you’re operating within a model’s context window, but as you add more examples, reference documents, or conversation history, you approach token limits.

    When a prompt that worked perfectly at 2,000 tokens hits a model’s 8,000-token context ceiling, the AI starts truncating your input or skipping steps. You won’t get an error—just incomplete output.

    Operators who save prompts without noting the surrounding context (how much history was in the thread, how large the input document was) often can’t reproduce results later. The prompt is the same, but the conditions aren’t.

    Your business needs evolve faster than your prompts

    A prompt you wrote in February to draft newsletter intros might have assumed a specific audience size, tone, or content format. Six months later, your subscriber count has doubled, your niche has narrowed, and your voice has shifted.

    The prompt still runs. It just produces output for a business that no longer exists.

    This is the subtlest form of staleness. The AI isn’t broken, and the model hasn’t changed. You have. But because the prompt still works, you keep using it—and wonder why the output feels off.

    How to keep your prompt library functional

    Date every prompt when you save it. Add a line at the top: Created: 2026-07-09 | Model: claude-3-5-sonnet | Context: 3K tokens. When you return to it three months later, you’ll know whether it’s worth running as-is or needs a refresh.

    Test your top five prompts monthly. Pick the ones you use most—content outlines, email drafts, research summaries—and run them with fresh input. If the output has drifted, you’ll catch it early.

    Version your prompts like code. When you tweak a prompt, save the new version separately. Don’t overwrite the original. If the update makes things worse, you can roll back. A simple naming convention works: landing-page-draft-v1, landing-page-draft-v2.

    Document what the prompt assumes. If it expects a specific input format (a bulleted list, a 500-word block, a CSV), note that. If it works best with a certain model or context size, write it down. Future you will thank present you.

    When to retire a prompt

    If you haven’t used a prompt in 60 days, archive it. Don’t delete it—just move it to a separate folder. Stale prompts clutter your library and tempt you to reuse output patterns that no longer fit your business.

    If a prompt requires more than two rounds of editing to produce usable output, rewrite it. The goal of a prompt library is speed. If you’re spending ten minutes massaging AI output every time, the prompt isn’t saving you time—it’s costing you focus.

    Want to see how other operators manage their AI workflows? Reply with your biggest prompt-library frustration—we’ll feature the best answers in an upcoming issue.

    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.

  • SEO traffic declines after switching hosts—DNS TTL is why

    SEO traffic declines after switching hosts—DNS TTL is why

    SEO traffic declines after switching hosts—DNS TTL is why
    Photo by Justin Morgan on Unsplash

    Move a WordPress site to a new host, watch organic traffic drop 15–30% for three to five days, then recover. If you’ve migrated hosting recently, you’ve probably seen this pattern. The culprit isn’t downtime, broken redirects, or angry Google crawlers—it’s DNS Time To Live (TTL) settings interacting badly with search engine cache behavior.

    Most solo operators treat host migration as a one-afternoon task: export database, move files, update DNS A record, wait. But DNS propagation isn’t instant, and search engines don’t immediately trust the new IP. The gap between those two realities creates a window where crawlers hit stale IPs, time out, or mark pages unreachable.

    What TTL does during migration

    DNS TTL tells browsers and crawlers how long to cache your domain’s IP address before checking again. If your TTL is set to 86400 seconds (24 hours)—a common default—and you migrate hosting, visitors and bots will keep hitting the old IP for up to a full day after you update DNS.

    That’s fine for human visitors using browsers with retry logic. It’s terrible for search crawlers operating on tight schedules. Googlebot doesn’t wait around. If your old host shuts down the account before TTL expires, crawlers see timeouts. Timeouts signal unreliability. Unreliability triggers temporary ranking suppression while Google reassesses your site’s health.

    The fix is simple but requires planning a week ahead: lower your DNS TTL to 300 seconds (five minutes) at least 48 hours before migration. This ensures that by the time you flip the DNS A record, cached lookups expire within minutes, not hours.

    Why old hosts break this silently

    Most shared and managed WordPress hosts auto-cancel accounts within hours of migration completion. You export your files, move DNS, and assume the old server stays online long enough for TTL to expire. It doesn’t.

    If your old host deactivates your account six hours after DNS changes, but your TTL is still set to 24 hours, a large percentage of cached DNS lookups will resolve to a dead IP for the next 18 hours. Crawlers see repeated failures. Analytics show a sudden drop in organic sessions. By the time TTL expires and traffic recovers, you’ve already lost several days of visibility in search results.

    Hosts rarely warn you about this because DNS management sits outside their direct responsibility—it’s handled by your registrar or a separate DNS provider like Cloudflare. The coordination gap means the problem only surfaces after migration is complete.

    The pre-migration checklist

    Start by logging into your DNS provider (Cloudflare, your registrar, or wherever your nameservers point). Locate the A record for your root domain and www subdomain. Check the TTL value—if it’s above 3600 seconds (one hour), lower it to 300.

    Wait 48 hours. This gives the current high TTL time to expire across the internet’s DNS cache layers. If you skip this step and lower TTL five minutes before migration, the old 24-hour TTL is still active everywhere—your change won’t take effect in time.

    Migrate your site. Update DNS A records to the new host’s IP. Keep the old hosting account active for at least 24 hours after DNS changes, even with the low TTL. This provides a safety buffer for any lagging DNS resolvers or aggressive caching layers.

    Monitor Google Search Console’s crawl stats and Coverage report during the 72 hours post-migration. If you see a spike in server errors or timeouts, your TTL window was too short or the old host deactivated too early. You can’t fix it retroactively, but you’ll know what broke.

    When managed hosts handle DNS

    Some managed WordPress hosts—Flywheel, WP Engine, Kinsta—offer DNS management as part of their platform. If your domain’s nameservers point directly to your host, TTL control sits inside their dashboard, and migration becomes trickier.

    These hosts typically manage TTL automatically, and you may not have manual override. The workaround: move DNS to a standalone provider (Cloudflare’s free tier works) at least a week before migration. Point your domain’s nameservers to Cloudflare, replicate all DNS records, lower TTL, then migrate hosting. It adds a step, but it gives you control over the timing.

    If you’re migrating to a managed host that includes DNS, ask support whether they lower TTL automatically during migrations. Most don’t. If they can’t confirm, assume you need to handle it manually.

    One operator I work with migrated a 40,000-session-per-month site from shared hosting to BigScoots in June. They followed the 48-hour TTL reduction, kept the old account live for 36 hours post-migration, and saw zero crawl errors in Search Console. Organic traffic dipped 4% for two days—within normal daily variance—then returned to baseline. Total cost of the precaution: two extra days of overlap billing at $6. Cost of not doing it: potentially two weeks of suppressed rankings while Google re-crawls and re-establishes trust.

    If you’re planning a host migration in the next quarter, add “lower DNS TTL 48 hours early” to your runbook now. It’s the one step that prevents an invisible SEO penalty most operators don’t connect to hosting until it’s too late.

    Have a hosting or infrastructure question you’d like covered? Reply to this email—I read every one.

  • WordPress plugin auto-updates break staging—here’s the override

    WordPress plugin auto-updates break staging—here’s the override

    WordPress plugin auto-updates break staging—here's the override
    Photo by Fikret tozak on Unsplash

    WordPress auto-updates shipped as opt-in for plugins in 5.5, then flipped to opt-out for most managed hosts by 2025. The feature works—until you run a staging environment.

    The problem: auto-update preferences are stored in the database, not in code. When you enable auto-updates for a plugin on production, then clone your site to staging for testing, staging inherits that same setting. If staging pulls an update first and you later sync the database back to production, you’ve just pushed untested code live without realizing it.

    Most operators discover this after a layout breaks or a payment form stops submitting. The fix isn’t intuitive, because the WordPress dashboard shows auto-updates as a per-plugin toggle with no environment awareness.

    Where auto-update state lives

    Plugin auto-update preferences are stored in the wp_options table under the key auto_update_plugins. It’s a serialized array of plugin basenames. When you enable auto-updates for a plugin, WordPress adds its basename (e.g., akismet/akismet.php) to that array. When you clone a database, you clone that array.

    Managed hosts like WP Engine, Kinsta, and BigScoots run their own update schedules—usually twice daily. If your staging site is on the same host infrastructure and shares the same cron timing, it can pull updates before production does. The update itself isn’t the issue; it’s the accidental database sync afterward that surfaces the problem.

    This is distinct from WordPress core auto-updates, which are controlled by the AUTOMATIC_UPDATER_DISABLED and WP_AUTO_UPDATE_CORE constants. Those live in wp-config.php and can be set per environment. Plugin auto-updates don’t have an equivalent constant—yet.

    The config-level override

    You can disable plugin auto-updates entirely on staging using a filter in your theme’s functions.php or a custom plugin. Add this to your staging environment only:

    add_filter( 'auto_update_plugin', '__return_false' );

    This filter runs before WordPress checks the auto_update_plugins option, so it overrides any database settings. Your staging site will never auto-update plugins, even if the dashboard toggle is on. Production can still auto-update as configured.

    If you want more control—say, allow auto-updates for specific plugins on staging but block others—you can inspect the $update object passed to the filter:

    add_filter( 'auto_update_plugin', function( $update, $item ) {
      $allowed = [ 'akismet/akismet.php', 'hello-dolly/hello.php' ];
      return in_array( $item->plugin, $allowed );
    }, 10, 2 );

    This approach whitelists only the plugins you name. Everything else stays manual.

    The deployment risk most operators miss

    The inverse scenario is equally common: you disable auto-updates on production because you want full control, then push your database to staging for testing. Staging now also has auto-updates disabled—but you wanted staging to pull updates automatically so you could test them before manually applying to production.

    The cleanest fix is to treat auto-update state as environment-specific config, not content. That means:

    • Set the filter in code, per environment
    • Ignore the dashboard toggle, or document that it’s overridden
    • Never sync the auto_update_plugins option between environments

    If you’re using a database sync tool like WP Migrate or All-in-One WP Migration, add auto_update_plugins to your exclusion list. Most tools let you skip specific option keys during import.

    When auto-updates make sense

    For solo operators running a single production site with no staging, auto-updates work well for low-risk plugins: Akismet, Yoast SEO, contact forms. The risk is low because you’re already trusting the plugin author’s release process, and critical security patches ship faster than you’d apply them manually.

    For sites with custom plugins, membership logic, or payment processing, the calculus flips. An auto-update that changes a filter priority or deprecates a function can break checkout flows without warning. Staging exists to catch that—but only if staging doesn’t push updates back to production through an accidental database sync.

    The non-obvious tip: if you run multiple staging environments (e.g., one for content review, one for plugin testing), disable auto-updates on the content-review staging and enable them on the plugin-test staging. That way your content team never sees surprise layout changes, and your dev environment always has the latest code to test against.

    Want more WordPress operations breakdowns? Reply with the hosting or plugin behavior you want explained next—we cover one every Monday.