Author: onetwothreeadmin

  • AI prompt templates fail when context drifts—version them

    AI prompt templates fail when context drifts—version them

    AI prompt templates fail when context drifts—version them
    Photo by Daria Nepriakhina 🇺🇦 on Unsplash

    If you’ve built a library of AI prompts that worked beautifully in March and now produce garbage in July, you’re not alone. The problem isn’t the model—it’s context drift, and most solo operators don’t version their prompts the way they version code.

    AI models change. Your business changes. The examples you fed into a prompt six months ago referenced products you no longer sell, a tone you’ve since abandoned, or input data structured in a format you’ve updated twice. When you paste that prompt into Claude or ChatGPT today, it misfires—and you waste twenty minutes editing output instead of moving on.

    Here’s how to version AI prompts so they stay useful, and when to retire them entirely.

    Why prompts degrade faster than you think

    Three things break prompts over time:

    • Model updates. OpenAI and Anthropic ship new versions every few months. A prompt optimized for GPT-4 in early 2026 may produce different results on the July release, even if the underlying capability improved. Temperature defaults, token handling, and instruction-following behavior all shift.
    • Your own vocabulary drift. You wrote a prompt template in February using placeholder variables like {{product_name}} and {{target_audience}}. By June, you’ve segmented your audience into three tiers, renamed your flagship product, and introduced a new content format. The old prompt doesn’t know any of this.
    • Corpus updates. If your prompt references specific URLs, doc IDs, or brand names, and any of those change, the AI hallucinates or defaults to generic output. A prompt that said “tone should match our About page at example.com/about” fails silently when you redesign the site and move that content.

    The result: you keep a folder of prompts, reuse one that used to work, and spend more time fixing the output than if you’d written from scratch.

    How to version prompts like code

    Treat each prompt as a versioned artifact. When you create or update a prompt that you’ll reuse, save it with a version number and a changelog note. This doesn’t require Git—a plain text file or a Notion doc works fine.

    Example structure:

    social-caption-v3.txt
    Last updated: 2026-07-15
    Changes: Removed reference to discontinued course; added instruction to include CTA link; clarified character limit to 280.

    When the prompt stops working well, duplicate it, update it, and increment the version. Keep the old one in an archive folder. If the new version performs worse, you can roll back and compare what changed.

    This costs you thirty seconds per update. It saves you fifteen minutes every time you revisit the prompt and wonder why it’s producing weaker output than you remember.

    Test prompts on sample data before you commit

    Before you version and archive a prompt, run it against three sample inputs that represent real use cases. Save the outputs. This creates a regression test.

    When you update the prompt, run the same three samples again. If the new version produces noticeably worse results on any of them, you’ve caught a regression before it cost you production time.

    This is especially useful for prompts that generate structured output—JSON, CSV, or formatted tables. A small wording change can break parsing logic downstream.

    When to retire a prompt entirely

    Not every prompt should be versioned forever. If you haven’t used a prompt in sixty days, it’s probably no longer relevant. Archive it separately or delete it.

    If you’ve versioned the same prompt four or five times and each version required substantial rewrites—not just tweaks—the underlying task has probably evolved beyond what a single template can handle. At that point, you’re better off writing fresh prompts on demand or splitting the task into smaller, more stable sub-prompts.

    Versioning is useful when the task is stable but the context shifts. If the task itself is unstable, the prompt library becomes clutter.

    One small addition that prevents most drift

    Add a dateline to every prompt: Context as of: July 2026.

    This reminds you—and the AI—that the instructions were written for a specific moment. When you revisit the prompt six months later, that dateline signals that you should review it before running it. It’s a forcing function that costs zero tokens and prevents silent degradation.

    Prompt versioning isn’t glamorous. But if you’re running a content business and relying on AI for drafts, summaries, or structured data extraction, unversioned prompts are technical debt. You’ll pay it back in wasted output and rework time.

    Got a prompt versioning system that works for you? Reply and tell us—we’ll feature operator workflows in a future piece. And if you want more AI tool breakdowns like this, subscribe to One Two Three Send for weekly deep dives on the tools solo operators actually use.

    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 post revisions database bloat: when to cap and purge

    WordPress post revisions database bloat: when to cap and purge

    WordPress post revisions database bloat: when to cap and purge
    Photo: Simone Bignetti via Wikimedia Commons (CC BY-SA 4.0)

    WordPress saves every draft change you make as a post revision. Every autosave, every manual save, every time you click update. Those revisions live forever in the wp_posts table unless you explicitly limit or purge them.

    For a solo operator publishing twice a week, this rarely matters. For a site with daily posts, guest contributors, or heavy editing cycles, you can end up with tens of thousands of revision rows that slow down queries, inflate backups, and make database migrations painfully slow.

    Here’s when revisions become a problem, how to cap them, and what happens when you purge old ones.

    How revisions accumulate faster than you think

    WordPress creates a new revision on every save—manual or automatic. The default autosave interval is 60 seconds. If you spend 20 minutes editing a post and save manually three times, you’ve just created roughly 23 revisions for a single post.

    Multiply that across a year of publishing, especially if you have multiple authors or use tools that auto-update posts (like content refreshers or dynamic blocks), and you’re looking at 5,000–15,000 revision rows per year for a modest site.

    Each revision is a full duplicate of the post content at that moment—title, body, meta. It’s not a diff. That’s why a site with 500 published posts can have 8,000 rows in wp_posts.

    When revisions start causing real problems

    Revisions don’t directly slow down your front end—visitors never query them. But they do three things that hurt:

    • Admin dashboard queries slow down. The post editor loads all revisions for the current post. If a single post has 200 revisions, that’s a noticeable lag when you open it.
    • Backup file sizes balloon. Your nightly database backup includes every revision. A 50 MB database becomes 200 MB, which slows down both the backup process and restores.
    • Database exports and migrations take longer. If you’re moving hosts or cloning a site, you’re waiting for thousands of unnecessary rows to export and import.

    You’ll notice the impact most clearly when running a plugin like WP-Optimize or querying the database directly. A SELECT that should return 500 posts instead scans 8,000 rows because revisions share the same table.

    How to cap revisions going forward

    Add this line to your wp-config.php file, anywhere above the line that says /* That's all, stop editing! */:

    define( 'WP_POST_REVISIONS', 5 );

    This limits WordPress to keeping the five most recent revisions per post. Older revisions are automatically deleted when a new one is created.

    Five is a sensible default for most solo operators. It’s enough to undo a bad edit or recover from an accidental overwrite, but not so many that you’re hoarding years of draft history you’ll never look at.

    If you want to disable revisions entirely, set it to false:

    define( 'WP_POST_REVISIONS', false );

    This stops all future revisions. Autosave still works—you can still recover unsaved changes—but WordPress won’t store a permanent history.

    How to purge existing revisions

    Capping revisions only affects new saves. It doesn’t touch the thousands of old revisions already sitting in your database.

    To remove those, you have two options: a plugin or a direct SQL query.

    Plugin method: Install WP-Optimize (free) or WP Sweep (free). Both have a one-click option to delete all post revisions. WP-Optimize also lets you schedule automatic cleanups weekly or monthly.

    SQL method: If you’re comfortable with database access, run this query in phpMyAdmin or your host’s database tool:

    DELETE FROM wp_posts WHERE post_type = 'revision';

    This deletes every revision from your database immediately. Make sure you have a backup first—this is irreversible.

    After running the query, also run OPTIMIZE TABLE wp_posts; to reclaim the disk space and rebuild the table index. Without this step, your database file size won’t actually shrink.

    The tradeoff: losing granular undo history

    Capping or purging revisions means you can’t travel back to a draft from six months ago. For most operators, that’s fine—you’re not auditing edit history or rolling back to ancient versions.

    But if you’re running a site with compliance requirements, guest contributors who might dispute changes, or content that frequently gets reverted (like policy pages or legal disclaimers), keep more revisions or use a version-control plugin like Revisionary or WP Document Revisions instead.

    For everyone else, five revisions and an annual purge is a reasonable maintenance habit that keeps your database lean without sacrificing practical undo capabilities.

    If you’re dealing with WordPress performance issues beyond revisions—caching, plugin conflicts, or database optimisation—reply and let me know what’s slowing you down. I’ll cover it in a future piece.

  • Social media automation token expiry: when connections break silently

    Social media automation token expiry: when connections break silently

    Social media automation token expiry: when connections break silently
    Photo by Deng Xiang on Unsplash

    Most social media automation tools connect to platforms using OAuth tokens. These tokens grant permission for one app to post, read analytics, or manage content on your behalf. They’re invisible until they stop working.

    The problem: tokens expire. Some platforms revoke them after 60 days of inactivity. Others refresh them automatically—until they don’t. When a token dies, your scheduled posts vanish into the void, your cross-posting stops, and you won’t know until you check manually or a follower asks why you went quiet.

    Here’s what actually happens, platform by platform, and how to catch failures before they cost you a week of missing content.

    How long tokens last, and what triggers expiry

    Twitter/X: Access tokens don’t expire on a fixed schedule, but the platform can revoke them if you change your password, revoke app permissions, or if Twitter detects suspicious API activity. In practice, tokens last months—until they don’t. No warning email.

    LinkedIn: Access tokens expire after 60 days. Refresh tokens last 12 months. If your automation tool doesn’t request a new access token within that 60-day window, the connection dies. LinkedIn sends no notification when this happens. Your posts just stop going out.

    Facebook/Instagram (Meta): Short-lived tokens expire in one hour. Long-lived tokens last 60 days. Most tools exchange short for long automatically, but if you don’t generate user activity within 60 days, the token goes stale. Meta’s Business Suite sometimes emails you, but the Developer dashboard doesn’t surface token health unless you check manually.

    YouTube: Refresh tokens don’t expire unless revoked manually or the account is inactive for six months. Google sends no proactive alert.

    The common thread: expiry is silent. Platforms assume the app developer—not you, the end user—will handle monitoring.

    What breaks when a token dies

    Most automation tools fail gracefully in the UI—they’ll show “connection lost” or “re-authenticate”—but they don’t always notify you by email or Slack. If you’re not logging into the tool daily, you won’t see it.

    Here’s what stops working:

    • Scheduled posts queue locally but never publish
    • Cross-posting from RSS feeds or your CMS halts
    • Analytics dashboards stop updating
    • Auto-replies, comment moderation, or DM automation freeze

    Some tools retry silently for 24–48 hours, then mark the post as failed. Others drop it entirely. Publer surfaces connection errors in its dashboard and sends email alerts if a publish attempt fails, but only if you’ve enabled notifications in settings—it’s off by default.

    Buffer queues failed posts and flags them, but won’t re-attempt after the connection is restored unless you manually reschedule. Hootsuite logs errors in the activity feed, but doesn’t push a notification unless you’ve configured a webhook.

    How to catch token expiry before it breaks your workflow

    Enable failure notifications in your scheduler. Every tool has this setting buried somewhere. Turn on email or Slack alerts for failed publishes, not just successful ones.

    Set a monthly calendar reminder to check connected accounts. Log in, review the integrations page, and confirm each platform shows “connected” with a recent timestamp. If a token is about to expire, some tools show a yellow warning icon—but only if you’re looking.

    Test with a throwaway post once a month. Schedule a post to all connected accounts for immediate publish, then verify it went live. Delete it 60 seconds later. This forces the tool to use the live token and surfaces any silent failures.

    Use a secondary monitoring tool if your workflow is critical. If you’re running a client account or a high-frequency publishing schedule, set up a simple uptime monitor that checks your social profiles for new posts. services like UptimeRobot or a custom Zapier workflow can ping you if no new content appears within an expected window.

    When to re-authenticate vs. when to rebuild the connection

    If your tool shows “re-authenticate,” clicking the button usually refreshes the token without losing your queue or settings. Do this immediately.

    If the re-auth flow fails—common with LinkedIn and Meta—you’ll need to fully disconnect and reconnect the account. This often clears your scheduled post queue for that platform. Before disconnecting, screenshot or export your queue. Most tools don’t preserve drafts when you sever a connection.

    Some operators keep a backup auth token in a separate tool (e.g., one account connected in Buffer, the same account also connected in Publer) so if one breaks, the other can cover while you troubleshoot. This adds overhead, but it’s cheaper than losing a week of posts.

    Want to avoid these silent breaks? Subscribe to One Two Three Send for operator-focused breakdowns of what actually fails—and how to fix it before it costs you traffic.

    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.

  • ConvertKit broadcast send-time personalisation: how it decides

    ConvertKit broadcast send-time personalisation: how it decides

    ConvertKit broadcast send-time personalisation: how it decides
    Photo by Kit (formerly ConvertKit) on Unsplash

    ConvertKit’s broadcast scheduler includes a checkbox labelled “Optimise send time.” When enabled, the platform doesn’t deliver your email at the moment you hit publish—it queues each subscriber’s copy for a window it thinks they’re most likely to engage.

    The feature sounds useful. In practice, it works well for some operators and creates confusion for others. Here’s what actually happens under the hood, when the algorithm helps, and when you’re better off choosing a fixed send time.

    What the algorithm looks at

    ConvertKit’s send-time optimisation scans each subscriber’s engagement history: opens, clicks, and the timestamps associated with both. It looks for patterns—does this person consistently open emails around 7 a.m. Eastern? Do they engage more on weekday mornings or weekend afternoons?

    If the platform identifies a statistically significant pattern, it schedules delivery within a window it predicts will perform better than a one-size-fits-all broadcast time. If no pattern exists—new subscribers, inactive readers, or those with erratic habits—it defaults to your account’s standard send time or the time you manually set when creating the broadcast.

    The optimisation window spans roughly 24 hours. ConvertKit won’t hold a broadcast for days, but it will stagger delivery across morning, afternoon, and evening slots depending on subscriber behaviour.

    When it helps

    Send-time optimisation works best when you have a large, engaged list with diverse time zones and consumption habits. If you’re sending to 10,000 subscribers spread across North America, Europe, and Asia, the feature can lift open rates by 2–8% compared to a single fixed time.

    It’s also useful when your content isn’t time-sensitive. Evergreen tutorials, weekly roundups, and educational sequences don’t lose value if they arrive six hours later than your publish click. The algorithm prioritises engagement over synchronicity.

    Operators who publish frequently—three or more broadcasts per week—also see better results. The platform accumulates more engagement data, which sharpens its predictions. If you send once a month, there’s less signal to work with.

    When manual scheduling wins

    Time-sensitive broadcasts break the optimisation logic. If you’re announcing a product launch, a limited-time discount, or commentary tied to a news event, you want simultaneous delivery. Staggering emails across 24 hours means some subscribers see the offer after it’s expired or the news cycle has moved on.

    Small lists—under 1,000 subscribers—don’t benefit much either. The algorithm needs volume to identify statistically meaningful patterns. With a few hundred people, manual scheduling based on your own audience knowledge often outperforms the automated approach.

    And if your list skews heavily toward a single time zone or demographic, the optimisation adds complexity without much upside. A newsletter serving U.S. East Coast professionals during weekday work hours doesn’t need personalised delivery windows—everyone’s already in the same behaviour bucket.

    One non-obvious tip

    ConvertKit’s send-time optimisation relies on opens as its primary engagement signal, but open tracking has degraded since Apple’s Mail Privacy Protection rolled out in 2021. A meaningful percentage of your subscribers now register artificial opens the moment an email hits their inbox, regardless of when they actually read it.

    This skews the algorithm’s predictions. If you notice erratic or counterintuitive delivery patterns—emails going out at odd hours, open rates drifting downward despite optimisation being enabled—try disabling the feature for two or three broadcasts and compare performance. Manual scheduling at a consistent, tested time often performs better when open-tracking reliability is compromised.

    You can also cross-reference your ConvertKit open data with click data, which remains accurate. If the platform says a subscriber opens at 6 a.m. but consistently clicks at noon, the optimisation may be misfiring. In that case, fall back to manual control.

    Want more breakdowns like this? Subscribe to One Two Three Send for weekly deep-dives on the tools and tactics that actually move the needle for solo operators.

  • SEO title tags longer than 60 characters: what Google actually shows

    SEO title tags longer than 60 characters: what Google actually shows

    SEO title tags longer than 60 characters: what Google actually shows
    Photo by Growtika on Unsplash

    Google’s title tag rewriting has gotten aggressive. Even when you write a clean, keyword-focused title, there’s no guarantee that’s what shows up in search results—especially if your title runs long.

    The old rule was simple: keep titles under 60 characters and you’re safe. That’s no longer true. Google now rewrites titles based on query context, page content, and its own interpretation of relevance. But character count still matters, and understanding where the cutoff happens—and what Google does when it hits that limit—can save you from losing clicks to ellipses or mangled rewrites.

    The 60-character guideline is a pixel width estimate, not a hard limit

    Google doesn’t count characters. It measures pixel width. A title with narrow letters like “i” and “l” can stretch past 70 characters and still display fully. A title packed with wide characters like “W” and “M” might get cut at 55.

    The commonly cited 60-character limit assumes an average character width of about 10 pixels, with a total display width around 600 pixels on desktop. Mobile widths are narrower—closer to 78 characters at roughly 920 pixels, but that varies by device and font rendering.

    Practically: if your title is under 60 characters, it’ll almost always display in full. Between 60 and 70, you’re gambling. Past 70, expect truncation or a rewrite.

    When Google truncates vs. when it rewrites entirely

    Truncation is the clean outcome. Your title gets cut mid-sentence and an ellipsis (…) appears. You lose the tail, but the front still represents what you wrote.

    Rewriting is messier. Google pulls text from your H1, meta description, or prominent on-page copy and generates a new title that may not match your intended keyword targeting at all. This happens more often when:

    • Your title is vague or keyword-stuffed
    • Your title doesn’t match the user’s query well
    • Your page has a strong H1 that differs significantly from the title tag
    • Your title exceeds roughly 70 characters and Google decides truncation would be unhelpful

    There’s no deterministic trigger. In testing across a few dozen client sites in early 2026, titles longer than 65 characters were rewritten about 40% of the time, while titles under 55 characters were rewritten less than 10% of the time—but only when the title and H1 were closely aligned.

    How to test what actually appears before you hit publish

    Don’t rely on SERP preview tools in your SEO plugin. They estimate based on character count, not actual Google rendering.

    The most reliable method: publish the page, wait 24–48 hours for indexing, then search for site:yourdomain.com "exact phrase from title" and see what Google shows. If it’s truncated or rewritten, you know before any traffic arrives.

    For faster iteration, use Google’s Rich Results Test or the URL Inspection tool in Search Console. Neither shows the exact SERP title, but they confirm whether your title tag is being read correctly and flag any HTML issues that might trigger a rewrite.

    If you’re split-testing title length, track CTR by title length cohort in Search Console. Filter by page, compare average position to CTR, and look for drop-offs where longer titles perform worse despite similar rankings. That’s your signal to trim.

    The non-obvious move: write for truncation, not against it

    Most operators try to stay under 60 characters to avoid truncation entirely. That’s safe, but it leaves opportunity on the table.

    If you front-load your title with the core keyword and value proposition in the first 50 characters, you can extend the tail with secondary keywords or modifiers that add context for Google’s algorithm—even if users never see them in the SERP.

    Example:
    Short, safe version (52 characters):
    “WordPress CDN setup: Cloudflare configuration guide”

    Extended version (68 characters):
    “WordPress CDN setup: Cloudflare configuration guide for speed & caching”

    The second version might get truncated to “WordPress CDN setup: Cloudflare configuration guide for spe…” in search results, but Google still indexes “speed” and “caching” as relevance signals. You get the ranking boost without sacrificing clarity in the visible title.

    This works best for informational content where secondary keywords reinforce the main topic. It’s riskier for commercial pages where every word in the SERP title needs to convert.

    Want more tactical breakdowns like this? Hit reply and tell us which part of your stack is giving you the most trouble right now—we’ll cover it in an upcoming issue.

  • Sponsored newsletter placements: actual CPM ranges by niche in 2026

    Sponsored newsletter placements: actual CPM ranges by niche in 2026

    Sponsored newsletter placements: actual CPM ranges by niche in 2026
    Photo by mauRÍCIO SANTOS on Unsplash

    If you’re thinking about selling sponsorships in your newsletter, the first question is always: what should I charge?

    Most advice points to CPM—cost per thousand opens—but the ranges you’ll find online are either outdated or so wide they’re useless. Here’s what solo operators and small teams are actually charging in 2026, broken down by niche, list size, and placement type.

    Tech and developer newsletters: $30–$80 CPM

    Tech newsletters with engaged audiences—especially those covering specific tools, frameworks, or workflow topics—command some of the highest rates. A 5,000-subscriber list with a 40% open rate (2,000 opens) can charge $60–$160 for a primary placement.

    The upper end applies when your audience skews toward decision-makers: CTOs, senior engineers, product leads. If your readers are junior devs or students, expect $30–$50 CPM.

    Sponsors in this niche are typically SaaS tools, API platforms, or developer-focused services. They’re buying access to builders who have budget authority or influence over stack decisions.

    Finance, investing, and business: $40–$100 CPM

    Newsletters covering personal finance, investing strategies, or small-business operations sit in a similar range—or higher if the audience is verified high-net-worth.

    A business newsletter with 10,000 opens can charge $400–$1,000 per placement. Sponsors here include fintech apps, investment platforms, business insurance, and accounting tools.

    The key variable is audience income and intent. If your readers are actively managing portfolios or running businesses, CPMs climb. If they’re browsing general money tips, rates drop toward $25–$40.

    Marketing and creator tools: $25–$60 CPM

    Newsletters for marketers, solo operators, and content creators—like this one—typically see $25–$60 CPM. Sponsors are often other newsletters, SaaS tools, courses, or affiliate offers.

    A list with 8,000 opens might charge $200–$480 for a dedicated sponsor block. Secondary placements (classified-style listings or brief mentions) go for $15–$30 CPM.

    This niche is crowded, and many readers are also operators who know what sponsorships cost. That keeps rates honest but also means you need strong engagement metrics—open rates above 35% and click-through above 2%—to justify premium pricing.

    Lifestyle, wellness, and general interest: $15–$40 CPM

    Broader lifestyle newsletters—covering travel, productivity, wellness, or general personal development—sit at the lower end. CPMs range from $15 to $40 depending on engagement and audience demographics.

    A 15,000-open newsletter in this category might charge $225–$600 per sponsor. The challenge is that sponsors often want very specific audience segments (parents, remote workers, specific age ranges), and general-interest lists don’t always deliver that precision.

    Higher rates come from tight positioning. A newsletter for remote-working parents will out-earn a general productivity newsletter with the same open count.

    What actually moves the rate

    Niche matters, but three other factors determine what you can charge:

    • Engagement rate: Opens above 40% and clicks above 3% let you charge 20–30% more than average.
    • Audience intent: Readers who buy tools, subscribe to services, or make business decisions are worth more than passive readers.
    • Sponsor fit: A tight match between your content and the sponsor’s product doubles conversion and justifies premium pricing.

    If you’re just starting, aim for the lower end of your niche’s range. Once you have case studies showing sponsor ROI—real click-through and conversion data—you can move toward the top quartile.

    Where to list your availability

    Most solo operators start by mentioning availability in their newsletter footer or a dedicated sponsor page on their site. Tools like Swapstack, Paved, and SparkLoop’s ad network can connect you with sponsors, though they typically take 20–30% of the placement fee.

    Direct outreach to brands you already use or cover often yields better rates and longer-term relationships. Send a one-page media kit with your open rate, audience breakdown, and two pricing tiers: primary placement and secondary mention.

    If your newsletter runs on Beehiiv, their built-in ad network can match you with sponsors automatically once you hit 500+ subscribers, though CPMs through the network tend to sit at the lower end of these ranges.

    Want more data on what’s working in newsletter monetisation? Reply with your niche and list size—I’ll share what operators in similar categories are seeing.

    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 multisite subdomain DNS: how wildcard A records actually work

    WordPress multisite subdomain DNS: how wildcard A records actually work

    WordPress multisite subdomain DNS: how wildcard A records actually work
    Photo by Markus Spiske on Unsplash

    WordPress multisite with subdomains sounds elegant in theory: every new site gets sitename.yourdomain.com instead of yourdomain.com/sitename. But the DNS configuration required to make that work trips up even experienced operators, especially when a CDN sits in front.

    If you’ve ever added a new multisite subsite and found it unreachable for hours—or had some subsites resolve while others don’t—the issue is almost always in how your wildcard A record is configured, cached, or overridden.

    What wildcard DNS actually does

    A wildcard A record in your DNS zone looks like this:

    *.yourdomain.com. A 192.0.2.10

    That asterisk tells the nameserver: “for any subdomain that doesn’t have an explicit record, send traffic to this IP.” So when someone visits newsite.yourdomain.com, and you haven’t manually created a DNS entry for newsite, the wildcard catches it and routes it to your server.

    WordPress multisite then inspects the HTTP Host header, matches it to a subsite in the database, and serves the right content. The wildcard doesn’t create the site—it just ensures the request reaches your server.

    But here’s the catch: wildcard records have lower priority than explicit records. If you’ve previously created an A record for staging.yourdomain.com pointing elsewhere, the wildcard won’t override it. That explicit record wins.

    When propagation stalls and caching hides the issue

    DNS records have a Time-To-Live (TTL) value, usually between 300 seconds (5 minutes) and 86400 seconds (24 hours). When you add or change a wildcard A record, recursive resolvers—like those run by ISPs or Cloudflare’s 1.1.1.1—cache the old state until the TTL expires.

    If your previous wildcard record had a 24-hour TTL and you just changed the IP, some visitors won’t see the update for a full day. Your local machine might resolve correctly (because your DNS client happened to refresh), but your phone on cellular data still hits the old IP.

    This creates a debugging mirage: the site works for you, fails for others, and you can’t reproduce the issue. The fix is to lower your wildcard A record TTL to 300 seconds before making changes, wait for the old TTL to expire, then make the change. After it propagates, you can raise the TTL again if you want.

    The CDN wildcard gotcha

    If you’re using a CDN like Cloudflare, BunnyCDN, or Fastly, the wildcard DNS record alone isn’t enough. Most CDNs require you to explicitly enable wildcard proxying or add each subdomain to a whitelist.

    On Cloudflare, for example, wildcard DNS records (*.yourdomain.com) can be proxied (orange cloud), but only on Business or Enterprise plans. On Free and Pro plans, the wildcard record must be DNS-only (grey cloud). That means requests hit your origin server directly—no CDN caching, no DDoS protection, no automatic SSL for new subsites.

    If you’re on a CDN plan that doesn’t support wildcard proxying, you have two options:

    • Upgrade the CDN plan to enable wildcard SSL and proxying.
    • Pre-create DNS records for each subsite manually and proxy those individually. This works, but it defeats the automation benefit of multisite subdomains.

    BunnyCDN supports wildcard pull zones on all plans, but you need to configure the hostname pattern in the pull zone settings. If you skip that step, the CDN won’t know which origin to pull from, and requests fail with a 404 at the edge.

    Non-obvious tip: test with dig, not a browser

    When you’re debugging wildcard DNS, don’t rely on your browser. Browsers cache DNS internally, and your OS has its own resolver cache. Instead, use dig or nslookup to query the authoritative nameserver directly:

    dig @ns1.yourprovider.com randomsubdomain.yourdomain.com

    Replace ns1.yourprovider.com with one of your domain’s authoritative nameservers (find them with dig yourdomain.com NS). If the wildcard is configured correctly, you’ll see an A record response with your origin IP, even for a subdomain that doesn’t exist in WordPress yet.

    If dig returns NXDOMAIN, the wildcard isn’t live yet—either it hasn’t propagated, or it wasn’t saved correctly in your DNS provider’s dashboard.

    When to use multisite subdomains vs. subdirectories

    Subdomain multisite makes sense when you’re running distinct properties under one WordPress install—think a network of regional sites, or client sites you manage centrally. But if you’re a solo operator running one content brand, subdirectories (yourdomain.com/blog, yourdomain.com/shop) are simpler. You avoid DNS complexity, CDN wildcard restrictions, and SSL certificate headaches.

    Subdomain multisite also complicates migration. Moving to a new host means updating the wildcard A record and waiting for propagation. With subdirectories, you update one A record and you’re done.

    If you’re already committed to subdomain multisite and hitting DNS or CDN issues, the fix is usually one of three things: lower TTL, enable wildcard proxying on your CDN, or confirm the wildcard A record actually saved. Test with dig, not guesswork.

    Got a WordPress hosting or DNS question? Reply to this email—I cover one reader question every Sunday.

  • ChatGPT’s Custom Instructions feature: what it actually remembers

    ChatGPT’s Custom Instructions feature: what it actually remembers

    ChatGPT's Custom Instructions feature: what it actually remembers
    Photo by Andrew Neel on Unsplash

    ChatGPT’s Custom Instructions feature lets you define persistent context that carries across every new chat. For solo operators running content calendars, client briefs, or repetitive workflows, it’s supposed to eliminate the copy-paste ritual of re-stating your role, audience, and formatting preferences every single time.

    In practice, it works—but only within specific boundaries that aren’t surfaced in the UI. Understanding what the feature actually retains, when it gets overridden, and how token allocation works will determine whether it saves you time or quietly undermines your prompts.

    What Custom Instructions stores

    The feature splits into two text boxes: “What would you like ChatGPT to know about you?” and “How would you like ChatGPT to respond?” Each accepts up to 1,500 characters. That’s roughly 300–400 tokens, depending on vocabulary.

    The first box is for context: your role, business model, audience, constraints. The second is for output preferences: tone, structure, length, formatting rules.

    Both get prepended to every new conversation as invisible system-level instructions. They don’t appear in the chat transcript, but they consume part of the context window before your first user message even loads.

    This matters because ChatGPT-4’s context window is 8,192 tokens (standard) or 32,768 tokens (extended, via API or Plus with longer chats enabled). If your Custom Instructions use 400 tokens and your conversation history fills another 6,000, you’ve got roughly 1,800 tokens left for the next user prompt and model response combined. Long conversations will eventually push Custom Instructions out of active memory—the model still “sees” them in the system prompt, but prioritises recent turns.

    When instructions get ignored

    Custom Instructions apply to new chats only. If you edit them mid-conversation, the changes don’t retroactively alter the existing thread. Start a fresh chat to pick up the edits.

    They also don’t override explicit contradictions in your user prompt. If your Custom Instructions say “always respond in bullet points” but your message says “write this as a paragraph,” the user prompt wins. The model treats Custom Instructions as defaults, not mandates.

    This is useful when you need to temporarily deviate—run a one-off analysis in a different format—but it also means vague user prompts can dilute or ignore your standing instructions entirely. Specificity in the moment beats standing context.

    Finally, Custom Instructions don’t persist across different ChatGPT interfaces. They apply to the web UI and the iOS/Android apps, but not to API calls, plugins, or third-party wrappers. If you’re running ChatGPT via Zapier, Make, or a custom script, you’ll need to inject that context manually in each request.

    What to put in—and what to skip

    Effective Custom Instructions are narrow and structural, not aspirational. “I run a weekly newsletter about SaaS pricing for B2B founders” is useful. “I value creativity and outside-the-box thinking” is not—it’s too abstract to influence output in a measurable way.

    Good candidates for the context box:

    • Your primary business model and audience (e.g., “solo operator running a paid Substack on AI regulation”)
    • Constraints you apply consistently (e.g., “posts are 800 words, American English, no listicles”)
    • Tools or platforms you use regularly (e.g., “I use ConvertKit and WordPress, not Mailchimp or Wix”)
    • Terminology preferences (e.g., “call them ‘subscribers,’ not ‘users’”)

    Good candidates for the response box:

    • Structural defaults (e.g., “use H2 subheadings, no H3s”)
    • Tone boundaries (e.g., “conversational but not casual, no exclamation marks”)
    • Output length (e.g., “default to 600–800 words unless I specify otherwise”)
    • Formatting rules (e.g., “return HTML, not Markdown”)

    Skip anything that changes project-to-project. Don’t embed client names, specific article topics, or one-off formatting requests—those belong in the user prompt, not standing instructions.

    One non-obvious tip: version your instructions

    Custom Instructions have no built-in versioning or change log. If you tweak them and output quality shifts, you won’t have a record of what changed unless you save snapshots externally.

    Keep a simple text file or note with dated versions of your instructions. When you experiment—tightening tone, adding a structural rule, removing a constraint—log the edit and the date. If output degrades or drifts after a few weeks, you can diff versions and pinpoint what shifted.

    This is especially useful if you’re running ChatGPT in parallel with Claude or another model. Custom Instructions are ChatGPT-specific, but the principles transfer. A versioned reference file lets you port tested context patterns across tools without starting from scratch each time.

    If you’re using Custom Instructions already, reply and tell us what’s in yours—or what you’ve tried and removed. We’re cataloging what actually works for operators running content businesses, not just what the feature says it does.

    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.

  • Analytics pixel load order changes what conversion data you see

    Analytics pixel load order changes what conversion data you see

    Analytics pixel load order changes what conversion data you see

    Most solo operators install analytics pixels the same way: copy the snippet, paste it into a header tag manager or WordPress plugin, and move on. But the order those scripts execute changes which events get tracked, how sessions are attributed, and whether conversions appear at all.

    Load order isn’t a hosting quirk—it’s a silent filter on your data. When a payment confirmation fires before your analytics library finishes initializing, that conversion never gets logged. When a UTM parameter gets stripped by a redirect before your tracking pixel reads it, attribution breaks. And because these failures happen in milliseconds, you won’t notice until you’re reconciling Stripe revenue against reported conversions and the numbers don’t match.

    What happens when pixels fire out of sequence

    Most analytics platforms—Google Analytics 4, Plausible, Fathom, Mixpanel—need their base library loaded before any event tracking calls execute. If a custom event fires while the library is still downloading, it gets dropped. No error. No retry. The event just doesn’t exist in your dashboard.

    This happens most often on single-page apps, checkout flows, and any page where a conversion event fires on page load rather than user action. A typical failure case: you use a Zapier webhook to log a new subscriber in your CRM, and that webhook triggers a thank-you page redirect. If the redirect happens before GA4’s gtag('event', 'conversion') call completes, the conversion never reaches Google’s servers.

    Even when the base library loads in time, the order pixels fire relative to each other determines what data each one sees. If a Facebook Pixel fires before a UTM-stripping redirect, it captures source attribution. If it fires after, it logs the visit as direct traffic. Neither setup is “wrong”—but one gives you attribution data you can act on, and the other doesn’t.

    Where load order gets decided

    If you’re using a tag manager—Google Tag Manager, Matomo Tag Manager, or a WordPress plugin like Site Kit—load order is controlled by trigger rules and priority settings. Most operators leave these at default, which means tags fire in the sequence they were added. That’s fine until you add a new pixel six months later and it fires last, missing fast-loading events.

    GTM lets you set a “tag firing priority” number. Higher numbers fire first. If your GA4 base tag has priority 10 and a custom conversion event has priority 5, the event will try to fire before the library exists. Set the base tag to a higher number than any dependent event, and the problem goes away.

    In WordPress, most analytics plugins inject scripts into wp_head or wp_footer hooks. The order depends on plugin load sequence, which is alphabetical by folder name unless you force a specific order with a plugin like Code Snippets or a custom functions.php entry. If two plugins both hook into wp_head with default priority (10), the one whose folder name comes first alphabetically loads first.

    That’s why renaming a plugin folder or switching from “Google Analytics for WordPress” to “Site Kit by Google” can silently change your tracking setup.

    What breaks when checkout flows redirect too fast

    Payment processors and course platforms almost always redirect immediately after a transaction. Stripe Checkout, Gumroad, and most hosted course tools send the buyer to a confirmation URL within milliseconds of payment. If your analytics pixel hasn’t finished sending the conversion event by the time that redirect fires, the event is abandoned mid-request.

    The fix is to delay the redirect until tracking calls return a success callback—but most platforms don’t expose that hook. Stripe Checkout’s success_url is a hard redirect; there’s no “wait for analytics” option. The workaround is to fire the conversion event on the confirmation page itself, not the checkout page. That way, even if the redirect is instant, the event fires after the user lands on a stable URL.

    This also means your conversion tracking needs to deduplicate. If someone refreshes the thank-you page, the event fires again. GA4 handles this with a client ID and session timestamp; other tools require a transaction ID parameter you pass through the URL and check against a cookie or localStorage flag.

    How to audit what’s actually firing

    Open your browser’s network tab, filter by “analytics,” “gtag,” “pixel,” or the domain of your tracking provider, and reload the page. Watch the order requests appear. If a conversion event request fires before the base library’s analytics.js or gtag.js, you’ve found the problem.

    For checkout flows, test with network throttling enabled (Chrome DevTools lets you simulate slow 3G). If your conversion event only fires on fast connections, it’s a timing race you’ll lose on mobile networks.

    If you’re using Postmark or another transactional email service to send purchase confirmations, compare the timestamp of the email sent against the timestamp of the conversion event in your analytics dashboard. If the email consistently arrives before the event logs, your tracking is firing too late—or not at all.

    Want more operator-to-operator breakdowns of what actually works? Subscribe to One Two Three Send—one article daily, no fluff, no affiliate spam unless the tool genuinely fits the topic.

  • Zapier filter conditions fail when field types mismatch

    Zapier filter conditions fail when field types mismatch

    Zapier filter conditions fail when field types mismatch
    Photo by Patrick Martin on Unsplash

    Zapier filters are supposed to be simple: if this field equals that value, continue. If not, stop. But filters fail more often than you’d expect—not because your logic is wrong, but because the data types don’t match what Zapier expects.

    A subscriber count from your email platform might arrive as text instead of a number. An empty field might be null, an empty string, or the word “null.” Your filter sees the mismatch and silently stops the Zap. No error email. No log entry flagged as critical. Just… nothing.

    Here’s how to catch these failures before they cost you hours of debugging.

    How Zapier evaluates filter conditions

    Zapier’s filter step compares two values: the field from your trigger or previous action, and the reference value you type in. The comparison operator—equals, contains, greater than—determines how Zapier interprets both sides.

    When you choose “equals,” Zapier performs a string comparison by default. If your trigger sends a number but the comparison field expects text, the filter might pass or fail unpredictably depending on how the source app formats the data.

    Example: your email platform sends subscriber count as 1250 (integer). You filter for “greater than 1000” (text). Zapier converts both to strings and compares them lexicographically. "1250" is not greater than "1000" in string comparison—it’s less than "2". The filter fails, and your Zap stops.

    This happens most often with:

    • Numeric fields sent as strings (subscriber counts, order totals, timestamps)
    • Boolean fields sent as "true" or 1 instead of true/false
    • Empty fields that arrive as null, "", "null", or "N/A"
    • Date fields formatted inconsistently across apps

    Diagnosing type mismatches in Zapier’s task history

    When a Zap stops unexpectedly, open the task history and expand the filter step. Zapier shows you the exact values it compared, but it doesn’t label their types.

    Look for these red flags:

    • Quoted numbers: "42" instead of 42
    • Inconsistent empty values: sometimes blank, sometimes "null"
    • Boolean values as strings: "true" instead of true

    If you see quotes around a value that should be numeric, the source app sent it as text. If an empty field shows "" in one run and nothing in another, the app’s API is inconsistent.

    Zapier’s task history keeps logs for 14 days on paid plans (7 days on free). If you’re debugging intermittent filter failures, screenshot the mismatched values before they expire.

    Fixing type coercion with formatter steps

    The fix is to normalise field types before the filter step. Add a Formatter by Zapier action between your trigger and filter, then choose the Transform operation that matches your data:

    For numeric comparisons: Use “Numbers > Format Number” to convert text to integer or decimal. Set the input to your dynamic field, leave the format as “plain number,” and pass the output to your filter.

    For boolean checks: Use “Text > Default Value” to replace empty or inconsistent fields with a standard fallback. If your trigger sometimes sends "true", sometimes 1, and sometimes nothing, map it to "yes" or "no" explicitly.

    For empty-field handling: Use “Text > Default Value” again. Set your dynamic field as input, and specify a fallback like "none" or 0. This ensures your filter always compares against a known value, not an unpredictable empty state.

    For date comparisons: Use “Date/Time > Format” to standardise timestamps into ISO 8601 or Unix epoch format. Then compare them as numbers (epoch seconds) instead of strings.

    Each formatter step costs one task. For high-volume Zaps, that adds up—but it’s cheaper than manually re-running failed tasks or missing critical automations.

    When to use Paths instead of Filters

    If your filter logic has more than two branches—e.g., if subscriber count is under 500, do A; if 500–2000, do B; if over 2000, do C—use Zapier’s Paths feature instead of stacking multiple filters.

    Paths let you define multiple conditions in a single step, each with its own downstream actions. They’re clearer to debug, and they don’t silently stop your Zap if one branch doesn’t match—they just skip that path and try the next one.

    Paths are available on Zapier’s Professional plan and above ($49/month as of mid-2026). If you’re on a lower tier and need branching logic, you’ll need to chain Zaps with webhooks or upgrade.

    Test with edge cases, not happy-path data

    Most operators test Zaps with clean, complete sample data. That’s why type mismatches don’t surface until production.

    Before you turn a Zap on, manually trigger it with:

    • An empty field where you expect a number
    • A zero or negative value where you expect positive
    • A malformed date (e.g., "2026-13-01")
    • A boolean field set to an unexpected value ("false" as text, not false)

    Zapier’s test step only runs with the most recent trigger data. If that data is clean, your test passes—but your production Zap still breaks when messy data arrives.

    Want more debugging tactics for no-code workflows? Reply with the automation tool you use most—I’ll cover its failure modes in a future issue.