Author: onetwothreeadmin

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

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

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

    What WordPress media search actually indexes

    The WordPress media library search looks at exactly three fields:

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

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

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

    When it breaks down

    Three scenarios make the default search useless:

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

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

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

    What to do instead

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

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

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

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

    The non-obvious fix: enforce filename hygiene at upload

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

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

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

    When you actually need a DAM

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

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

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

  • Traffic attribution breaks when you run multiple campaigns at once

    Traffic attribution breaks when you run multiple campaigns at once

    Most analytics platforms promise to tell you exactly where your traffic came from. Google Analytics 4 shows you a tidy funnel with source, medium, and campaign labels. You see “organic search” or “Facebook / cpc” and assume you know what’s working.

    But if you’re running more than one acquisition channel at the same time—SEO content, a Google Ads campaign, and a LinkedIn presence, for example—your attribution data is almost certainly lying to you.

    The problem isn’t the tools. It’s that attribution models can’t handle the way real people behave online.

    Why multi-touch attribution falls apart

    Attribution models try to assign credit for a conversion across multiple touchpoints. GA4 offers data-driven attribution by default, which sounds sophisticated until you realize it’s still guessing.

    Here’s what actually happens: someone finds your site via organic search, reads a post, leaves. Three days later they see your LinkedIn post, click through, subscribe. Two weeks after that, they click a retargeting ad and buy your course.

    GA4 will credit the last non-direct click—the retargeting ad—even though the organic post did most of the work. If you switch to first-click attribution, you overweight SEO and ignore the fact that your ad closed the deal. Data-driven attribution tries to split the difference, but it’s still modeling behavior it can’t fully see.

    Cross-device journeys make it worse. If someone reads your newsletter on their phone and later converts on desktop, most analytics platforms treat those as two separate users unless you’re running a logged-in experience with user IDs.

    The result: you look at your attribution report and think Facebook is outperforming SEO, so you cut your content budget. Three months later, your paid campaigns stop converting because there’s no top-of-funnel content feeding them.

    What to track when attribution breaks

    Instead of trusting last-click or data-driven models, track channel performance in isolation and watch for correlation, not causation.

    Segment by landing page source. If most of your organic traffic lands on blog posts and most of your paid traffic lands on a dedicated landing page, you can compare conversion rates by entry point. It’s not perfect, but it’s more honest than pretending GA4 knows which click “caused” the sale.

    Use UTM parameters religiously, but don’t over-rely on them. Tag every link you control—social posts, email campaigns, guest articles. But remember that UTMs only tell you where the last click came from. If someone clicks your LinkedIn post, browses, leaves, and returns via direct traffic, the UTM is gone.

    Track assisted conversions separately. GA4’s “Advertising” workspace has an assisted conversions report that shows which channels appeared earlier in the funnel, even if they didn’t get last-click credit. It’s buried, but it’s one of the few reports that acknowledges multi-touch reality.

    Run channel blackout tests. This is the only way to measure true incrementality. Pause one channel completely for two weeks and watch what happens to your overall conversion volume. If you turn off paid ads and conversions drop by exactly the amount those ads were generating, they were working. If conversions stay flat, your paid traffic was cannibalizing organic visits that would have converted anyway.

    When to stop caring about attribution

    If you’re a solo operator pulling in less than $10K/month, attribution modeling is probably a distraction. You don’t have enough traffic or conversions to make statistical inferences, and you’re better off focusing on channel-level metrics: CPM for paid ads, click-through rate for email, time-on-page for SEO content.

    Attribution matters when you’re spending enough that a 10% efficiency gain is worth the analytical overhead. For most indie operators, that threshold is somewhere north of $5K/month in paid acquisition spend.

    Below that, track what you can measure cleanly—email open rates, organic impressions, ad spend per signup—and accept that the handoff between channels is a black box. Your job is to keep all the channels healthy, not to perfectly allocate credit between them.

    A better question than “which channel converted?”

    Instead of asking which channel gets credit for the sale, ask: which channels would cause revenue to drop if I turned them off?

    That’s a harder question to answer, but it’s the one that actually matters. It forces you to think in terms of systems, not funnels. Your SEO content feeds your email list. Your email list warms people up for your paid retargeting. Your retargeting closes deals that started with a Twitter thread six weeks ago.

    Attribution reports want to collapse that system into a single “winning” channel. In reality, the system only works when all the parts are running.

    If you want to get serious about measuring incrementality, set up holdout groups, run A/B tests at the channel level, and accept that you’ll never have perfect data. If you just want to grow your business, watch revenue per channel over time and invest more in whatever’s trending up—even if you can’t prove exactly why.

    Got a question about tracking, attribution, or analytics for your online business? Reply to this email—we read everything and answer the best questions in future issues.

  • Content calendars break when you plan more than two weeks out

    Content calendars break when you plan more than two weeks out

    Most productivity advice tells you to plan your content calendar weeks or months in advance. Map out topics, assign publish dates, block time for research and writing. The promise: less scrambling, more consistency, better results.

    The reality: those carefully planned calendars fall apart within days. A news cycle shifts. A tool launches. A reader question surfaces a better angle. Suddenly your polished four-week roadmap is obsolete, and you’re either publishing stale content or abandoning the plan entirely.

    Content calendars aren’t the problem. Over-planning is.

    Why long-range content planning fails solo operators

    When you’re running an online business alone or with a small team, you don’t have the luxury of a newsroom’s division of labor. You’re the reporter, editor, and publisher. That means your content strategy needs to respond to what you’re learning in real time—from analytics, reader replies, product feedback, and the tools you’re actually using day-to-day.

    A quarterly content calendar assumes your priorities won’t change. But if you’re iterating on a product, testing traffic channels, or adjusting your monetization model, your editorial focus should shift. Locking topics into a grid three months out creates artificial commitment to ideas that may no longer serve your business.

    There’s also the sunk-cost problem. Once you’ve invested an hour sketching out twelve topic ideas and slotting them into a spreadsheet, you feel obligated to execute them—even when a better, more timely topic emerges. The calendar becomes a constraint instead of a tool.

    The two-week threshold

    Two weeks is roughly the outer limit where topic planning still holds value without becoming a liability. You can reasonably predict:

    • Which tools you’ll be using or testing
    • What questions your audience is asking right now
    • What seasonal or industry events are genuinely imminent
    • Your own availability and energy levels

    Beyond that window, you’re guessing. And guesses dressed up as a content calendar waste time twice: once when you plan them, again when you revise or scrap them.

    For operators publishing multiple times per week, two weeks gives you four to eight slots. That’s enough runway to write ahead when inspiration strikes, but not so much that you’re locked into topics that go stale.

    What to plan instead of topics

    If you’re not filling a calendar grid with headline ideas, what do you schedule?

    Themes and beats, not individual pieces. Decide that this month you’ll rotate between three beats—say, email tools, AI workflows, and monetization tactics—without assigning specific angles. When it’s time to write, you pick the freshest idea within that beat.

    Formats and content shapes. Monday is a tutorial. Wednesday is a tool comparison. Friday is a case study with real numbers. The format is fixed; the topic flexes based on what’s relevant that week. This is how One Two Three Send operates, and it prevents both topic fatigue and the scramble for structure.

    Production blocks, not publish dates. Instead of “publish SEO guide on June 12,” schedule “research and draft one traffic piece between June 10–14.” You’re committing to the work, not the output. If a better angle surfaces mid-week, you can pivot without guilt.

    Evergreen buckets. Keep a running list of topics that aren’t time-sensitive. When you have energy but no urgent idea, pull from the bucket. These aren’t scheduled—they’re available.

    The calendar you actually need

    Your content calendar should answer one question: What’s the next thing I need to write, and when do I need to write it?

    For most solo operators, that’s a two-column tracker: publish date and content type. Fill the next two weeks. Leave everything else in a backlog, unsorted and unscheduled. Review the backlog weekly, pull in what’s relevant, let the rest age out.

    Tools don’t matter much here. A Notion database works. So does a Google Sheet, Trello board, or a text file. The system isn’t the point—the short planning horizon is.

    If you’re currently staring at a color-coded content calendar that runs through September, try this: archive everything more than two weeks out. For the next month, plan only the immediate next batch. Track whether you feel more scrambled or more responsive.

    Odds are, you’ll publish more relevant content with less planning overhead. And you’ll stop feeling guilty about ignoring a roadmap that was never going to survive contact with your actual business.

    What’s your content planning window right now—and is it working? Hit reply and let me know. I read every response, and reader questions often shape what we cover next.

  • AI assistants leak context when you switch between projects

    AI assistants leak context when you switch between projects

    If you’re using ChatGPT, Claude, or Gemini to draft emails, write product descriptions, and brainstorm content ideas in the same session, you’ve probably noticed the tone starting to bleed. A client brief starts sounding like your newsletter. Your course outline picks up jargon from a product spec you wrote an hour ago.

    That’s not you losing focus. It’s the assistant remembering too much.

    How AI memory works across conversations

    Most conversational AI tools maintain context within a single chat thread. That’s useful when you’re iterating on a draft or debugging a workflow. But many platforms now also persist memory across threads—storing facts, preferences, and patterns from past sessions to make future responses feel more tailored.

    ChatGPT’s memory feature, for example, stores details like your writing style, preferred tools, and recurring projects. Claude offers a similar feature called “custom instructions” that carries forward between chats. Gemini ties memory to your Google account, pulling in context from Gmail, Docs, and prior conversations.

    The problem: these tools don’t automatically segment memory by project, client, or business vertical. If you’re a solo operator juggling freelance work, your own newsletter, and a side product, the AI treats it all as one continuous job.

    That means a prompt like “write a welcome email” might generate copy that sounds like the SaaS client you were working with yesterday—not the D2C product you’re launching today.

    When bleed happens and why it matters

    Context bleed shows up most clearly in tone, vocabulary, and assumed audience. If you’ve been drafting B2B landing pages all morning and then ask the AI to write a casual Twitter thread, you’ll often get something that splits the difference: too formal for social, too chatty for a white paper.

    It’s worse when you’re working under NDA or handling sensitive client material. Even if you’re not pasting in proprietary data, the AI might pick up on industry-specific language, product names, or strategic framing and echo it back in unrelated work. That’s a compliance risk and a professionalism problem.

    For operators running multiple revenue streams—affiliate content, a paid newsletter, consulting, a course—context bleed also dilutes your brand. Your newsletter starts to sound like your client’s voice. Your course copy picks up affiliate-review phrasing. Readers notice when the tone shifts, even if they can’t articulate why.

    How to isolate context without losing efficiency

    The simplest fix: use separate chat threads for separate projects. Don’t rely on the AI to infer boundaries. Start a fresh conversation every time you switch contexts.

    If your AI tool supports memory or custom instructions, turn it off for client work. In ChatGPT, you can disable memory in settings under “Data Controls.” Claude lets you clear custom instructions per chat. Gemini’s memory is harder to partition, but you can use incognito mode or a separate Google account for client sessions.

    For operators who bill multiple clients or run distinct content verticals, consider using different AI accounts entirely. A free-tier ChatGPT account for client drafts, a paid Claude subscription for your own content. It’s redundant, but it enforces a hard boundary.

    Another tactic: explicitly reset context in your prompt. Start each session with a short instruction that overrides prior memory. Something like: “Forget previous projects. This is a cold email for a B2C e-commerce brand selling outdoor gear. Tone: direct, benefit-focused, no jargon.” It’s not foolproof, but it reduces bleed.

    When memory is worth keeping

    Context persistence isn’t inherently bad. If you’re working on a single long-term project—building out a course, drafting a book, developing a content calendar—memory helps the AI stay consistent without you having to repeat background in every prompt.

    The key is intentional segmentation. Treat AI memory like browser cookies: useful within a defined scope, risky when it leaks across domains.

    If you’re a solo operator, that means thinking through which projects share a voice and which need to stay isolated. Your newsletter and your Twitter threads? Probably fine to share context. Your newsletter and a white-label ghostwriting gig? Keep those separate.

    Most AI tools don’t yet offer project-level memory management. Until they do, the burden is on you to create those boundaries manually—through new threads, separate accounts, or hard resets in your prompts.

    One Two Three Send covers AI tools, workflow automation, and the infrastructure that keeps solo operators running. If this kind of tactical breakdown is useful, subscribe for one article like this every day.

    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.

  • Notion databases: when to relate tables and when to duplicate

    Notion databases: when to relate tables and when to duplicate

    Notion sells itself on the promise of connected databases—one source of truth, relational properties, rollups that calculate automatically. It’s a compelling pitch, especially if you’re managing clients, projects, content calendars, and invoices in the same workspace.

    But relational databases in Notion come with friction most solo operators don’t anticipate until they’re neck-deep in broken views and slow-loading pages. Sometimes the smarter move is to duplicate your data instead of relating it.

    When relations make sense

    Relational properties work well when you need live, two-way sync between databases. A classic example: a Projects database linked to a Clients database. You relate each project to a client, and Notion automatically shows all projects under that client’s page. Add a rollup, and you can calculate total hours billed or outstanding invoices without touching a spreadsheet.

    This setup shines when:

    • You frequently filter or group by the related property (e.g., “show me all active projects for Client X”).
    • The related database is relatively small—under 500 rows.
    • You need rollups or formulas that pull data from the related table.

    But the moment your related database grows past a few hundred entries, or you start chaining relations (Projects → Clients → Invoices → Line Items), Notion’s performance starts to sag. Pages take three to five seconds to load. Inline databases stutter when you apply filters. Mobile becomes nearly unusable.

    When duplication beats relation

    Duplication means copying static data—like a client name or project status—directly into the database where you need it, rather than linking to another table. You lose the automatic sync, but you gain speed and simplicity.

    Here’s when to duplicate:

    • High-volume databases. If your content calendar has 1,000+ rows and you’re relating each post to an author database, every view recalculates those links. Duplicating the author name as plain text makes filters instant.
    • Archival data. If you’re logging completed projects or past invoices, the data rarely changes. A relation adds complexity with no upside—just copy the client name and close the loop.
    • Cross-workspace collaboration. Notion’s database relations don’t work across workspaces. If you’re sharing a content calendar with a client but keeping your internal project tracker private, duplication is your only option.

    Yes, you lose the ability to update a client’s name in one place and see it cascade everywhere. But for most solo operators, that’s a theoretical problem. You’re not renaming clients every week. The performance gain is real; the sync risk is hypothetical.

    The hybrid approach

    You don’t have to choose one strategy for your entire workspace. I run a Content database with 800+ posts. Each post has a Topic property—but instead of relating to a separate Topics database, I use a multi-select dropdown. It’s technically duplication (the topic name lives in the Content database), but I can still filter, group, and count posts by topic without the relational overhead.

    For high-touch workflows—like tracking sponsorship deals where I need live rollups of total contract value—I keep the relation. For everything else, I flatten the data.

    Notion’s automation and API integrations (via Zapier or Make) can help maintain duplicated data if you do need occasional syncing. A zap can copy a client’s updated name into your archive database once a month, for example, without forcing every page load to recalculate a relation on the fly.

    What this means for your workspace

    If your Notion workspace feels sluggish, audit your relational properties first. Open your largest database, check the relations panel, and ask: Do I actually use this link, or am I just maintaining it because it feels like best practice?

    Relations are a feature, not a requirement. Treat them like indexes in a SQL database—powerful when you need them, expensive when you don’t.

    Using Notion to run your business? Reply with your biggest database headache—I’ll feature reader solutions in a future issue.

  • Canva’s content planner doesn’t play nice with multi-account workflows

    Canva’s content planner doesn’t play nice with multi-account workflows

    Canva rolled out its content planner feature to Pro subscribers in 2023, pitching it as an all-in-one design-and-schedule hub. You design a post, hit schedule, and Canva pushes it to Instagram, Facebook, LinkedIn, Pinterest, or TikTok at the time you choose. No export, no intermediate tool.

    It’s genuinely useful if you run a single brand with one social account per platform. But if you manage more than one business, run client accounts, or operate multiple brands under different identities, Canva’s content planner becomes a liability fast.

    One Canva account, one social identity

    Canva ties each social platform connection to a single account. You can link one Instagram account, one Facebook page, one LinkedIn profile. If you want to schedule to a second Instagram account, you need to disconnect the first, reconnect the second, schedule, then reverse the process.

    There’s no account switcher. There’s no team workspace toggle that lets you route posts to different clients’ accounts. Every disconnect-reconnect cycle risks losing your queue if you’re not careful, and it makes batch scheduling across brands impossible.

    For solo operators running two newsletters with separate social presences, or a freelancer managing three clients, this isn’t a minor inconvenience—it’s a structural mismatch.

    Brand kits don’t bridge the gap

    Canva lets Pro users create multiple brand kits: custom color palettes, fonts, and logos. You can swap between them when designing. But brand kits don’t extend to social account connections. Your Instagram link is account-wide, not brand-specific.

    You can design a post for Client A using their brand kit, then realize you’re still connected to Client B’s Instagram. The only fix is to save the design, disconnect, reconnect, find the design again, and reschedule. If you’re doing this more than twice a week, you’ll start looking for an alternative.

    Where dedicated schedulers win

    Tools like Publer, Buffer, and Later separate design from scheduling. You upload an asset, write copy, and assign it to one account in a dropdown. You can manage ten Instagram accounts, three LinkedIn profiles, and five Facebook pages in the same dashboard. Switching between them takes one click.

    Publer in particular handles this well: you connect multiple accounts per platform, tag each with a client or brand name, and filter your content calendar by account. Canva can’t replicate that because its content planner was built as a convenience feature for individual creators, not a workflow hub for multi-brand operators.

    The pricing delta is small. Canva Pro runs $120/year. Publer’s Premium plan is $12/month ($144/year) for up to ten social accounts and unlimited scheduling. If you’re already paying for Canva and need multi-account scheduling, the extra $24/year is negligible.

    When Canva’s planner still makes sense

    If you run one brand, post occasionally, and design everything in Canva anyway, the content planner is fine. It’s faster than exporting, uploading to another tool, and scheduling there. The calendar view is clean, the post preview is accurate, and it handles Instagram carousels without drama.

    But the moment you add a second account—whether that’s a personal brand alongside a business, a client project, or a side newsletter with its own social presence—the convenience evaporates. You’ll spend more time managing connection state than you save skipping an export step.

    If you’re already hitting that wall, set up Publer or Buffer now. Export your designs as PNGs, schedule them in a tool built for multi-account workflows, and stop fighting Canva’s architecture. The content planner is a feature, not a platform. Treat it that way.

    Want more tool breakdowns like this? Subscribe to One Two Three Send and get operator-focused guides every week—no fluff, just the mechanics that matter.

    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.

  • Substack’s paid subscriber import: how it works and what breaks

    Substack’s paid subscriber import: how it works and what breaks

    If you’re switching to Substack with an existing paid subscriber base, you’ll quickly discover that importing them isn’t a one-click operation. The platform treats free and paid subscribers very differently—and the tooling around paid imports is intentionally limited to protect both you and your readers from billing chaos.

    Here’s how the process actually works, what limitations you’ll hit, and the non-obvious gotcha that can cost you a month of revenue if you’re not careful.

    The two-path import: free vs. paid

    Free subscribers upload via CSV without friction. You export from your old platform, map the columns, and Substack ingests them in minutes. Paid subscribers require a different workflow because Substack needs to connect each person to an active billing relationship—either a Stripe subscription ID or a comped status you manually assign.

    If your previous platform used Stripe, Substack can import the subscription metadata directly—but only if both accounts share the same Stripe Connect relationship. That’s rare unless you were already using a Stripe-native platform like Ghost or a custom-built membership site. Most operators are coming from ConvertKit, Mailchimp, or Beehiiv, none of which expose raw Stripe subscription IDs in their CSV exports.

    That means you’ll use the comped subscriber route: you import paid subscribers as free accounts, then manually apply a “complimentary subscription” that grants them full access without charging them. Substack treats comped subs identically to paid ones for content access, but they don’t appear in your MRR calculations and won’t auto-renew unless you later convert them to a paid plan.

    The billing cutover problem

    Here’s the edge case that catches people: if you comp your existing paid subscribers and tell them to re-subscribe at their next renewal date, you’ll lose anyone whose renewal falls in the window between your import and your announcement. They’ll get charged by your old platform, then hit a paywall on Substack, and assume something broke.

    The safer approach is to cancel all subscriptions on your old platform before you import, refund any partial-month charges, and immediately comp everyone on Substack with an expiration date set to their original renewal. Then send a dedicated email explaining the transition and asking them to update their payment method before the comp expires. Substack will email them automatically seven days before expiration, but your own message converts better because it comes from you, not the platform.

    Expect 10–15% of comped subscribers to churn during this transition. That’s normal. The ones who don’t update their payment info within 30 days probably weren’t engaged enough to stay long-term anyway.

    The Stripe metadata you actually need

    If you do have access to Stripe subscription IDs—either because you’re migrating from Ghost, or because you were running Stripe directly—you’ll still need to provide Substack support with a CSV that maps each email address to its subscription ID and current billing cycle anchor date. You can’t upload this yourself; Substack’s backend team handles it manually to avoid mismatched billing states.

    Turnaround time is typically 3–5 business days, and they’ll only process it if your Stripe account is already connected to your Substack publication. If you’re switching Stripe accounts as part of the migration (common if you’re moving from a business entity to a personal one, or vice versa), you’ll need to use the comp method instead. Substack won’t connect a subscriber to a subscription ID that lives in a different Stripe account.

    When to skip the import entirely

    If you have fewer than 50 paid subscribers, the cleanest move is often to let them re-subscribe manually. Cancel their old subscriptions, refund the current billing period, and send a launch email with a discounted annual plan offer that offsets the hassle. You’ll lose some, but you’ll also avoid two weeks of support emails from people whose billing states didn’t migrate cleanly.

    For lists above 200 paid subscribers, the comp-and-convert method is worth the effort. Between 50 and 200, it depends on how hands-on you want to be and whether you’re also changing pricing or plan structure as part of the move.

    One last note: if you’re moving to Substack because you want their payment infrastructure to handle sales tax, VAT, and global compliance, make sure you’re not grandfathering in subscribers at old prices that don’t include tax. Substack’s tax automation only works on new subscriptions created after you enable it. Comped subscribers who convert will be charged tax, but anyone you migrate via Stripe ID handoff will keep their original tax treatment unless you manually update each subscription in Stripe. That’s a billing-support nightmare six months later when your accountant asks why half your EU subscribers aren’t remitting VAT.

    If you’re planning a platform migration and want step-by-step breakdowns like this every week, subscribe to One Two Three Send. We cover the tooling decisions that actually matter when you’re running a one-person content 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.

  • Google Analytics 4’s IP exclusion filter and why it still logs you

    Google Analytics 4’s IP exclusion filter and why it still logs you

    You add your office IP to Google Analytics 4’s exclusion filter, click save, and assume your internal visits stop polluting your traffic reports. Then you check a few days later and your test sessions are still showing up—or worse, they disappear from one report but linger in another.

    GA4’s IP exclusion doesn’t work the way Universal Analytics did. It doesn’t reject traffic at the door. It logs everything, applies a filter tag, and suppresses matching hits in most—but not all—standard reports. That distinction matters when you’re trying to get clean data or debug why your numbers don’t match what you see in server logs.

    How GA4’s IP filter actually works

    When you define an IP exclusion filter in GA4’s data stream settings, you’re not blocking requests. You’re telling Google to apply an internal_traffic parameter to any hit that matches your IP range. That parameter gets attached to the event payload and flows into BigQuery exports and debug logs, but it’s excluded from most GA4 interface reports by default.

    The key word is “most.” Explorations, custom funnels, and any report built with unfiltered dimensions can still surface internal traffic if you don’t manually apply a segment or filter. Real-time reports often show excluded IP hits for several minutes before suppression kicks in, because the tagging happens asynchronously.

    If you’re running a low-traffic site and testing conversion flows yourself, a single internal session can skew daily conversion rates by 20% or more—even if you think you’ve filtered it out.

    Why your IP still appears in some reports

    Three common reasons:

    • Dynamic IP assignment: If your ISP rotates your IP every few days, your exclusion rule goes stale. GA4 doesn’t notify you when this happens. You have to check your public IP periodically and update the filter manually, or use a broader CIDR range if your provider allocates from a known block.
    • Mobile and VPN traffic: Your phone’s carrier IP and any VPN endpoint won’t match your home or office range. If you test email links on mobile or browse your site through a VPN, those sessions aren’t tagged as internal unless you’ve added every possible IP.
    • Server-side GTM or Measurement Protocol hits: If you’re sending events via server-side Google Tag Manager or the GA4 Measurement Protocol, the IP GA4 sees is your server’s IP, not the end user’s. Your exclusion filter won’t match unless you explicitly pass the user’s IP in the payload and your server is on the excluded list.

    When to use IP exclusion vs. other filtering methods

    IP filtering works well for small teams working from fixed locations—a home office, a coworking space with a static IP, or a single cloud server sending backend events. It’s lightweight, doesn’t require changing your tagging code, and applies retroactively to all future hits.

    But if your team is distributed, remote, or frequently mobile, IP exclusion becomes a maintenance burden. A better approach: set a first-party cookie or localStorage flag when someone logs into your CMS or admin panel, then use Google Tag Manager to check for that flag and not fire the GA4 tag at all. This method blocks the hit entirely rather than tagging and suppressing it.

    For agencies or consultants who work across dozens of client sites, browser extensions like “Block Yourself from Analytics” (available for Chrome and Firefox) inject a query parameter or disable the GA4 script on sites you specify. You don’t have to ask every client to add your IP to their exclusion list.

    One non-obvious gotcha

    GA4’s IP exclusion filter doesn’t apply to Google Signals data or any cross-device user stitching. If you’re logged into a Google account on your work machine and browse your own site, Google may still associate that session with your broader user profile for audience building and attribution modeling, even though the session is tagged as internal traffic. The data won’t appear in your standard reports, but it can influence remarketing audiences and conversion credit if Google Signals is enabled.

    To fully exclude yourself from tracking and modeling, you need to either disable Google Signals in your property settings or browse in an incognito window while logged out of all Google accounts.

    IP exclusion in GA4 is not a firewall—it’s a post-collection label. If you need truly clean data and you’re testing frequently, combine IP filtering with client-side tag blocking and regular audits of your Realtime and Exploration reports. And if your IP changes more than once a month, automate the filter update or switch to a cookie-based exclusion method before your internal traffic becomes your largest segment.

    What’s your internal-traffic filtering setup? Reply and let us know what’s working—or what’s leaking through.

  • What Gumroad’s ‘pay what you want’ pricing actually earns you

    What Gumroad’s ‘pay what you want’ pricing actually earns you

    Gumroad’s pay-what-you-want (PWYW) pricing looks like a generous experiment until you run the numbers. The feature lets buyers name their own price above a floor you set—or no floor at all. The pitch is that generosity builds goodwill, increases conversions, and unlocks buyers who’d never pay full price.

    The reality is messier. PWYW can work, but only in narrow conditions. Most operators who flip the switch see average transaction values drop between 40% and 70% compared to fixed pricing, even when they set a suggested price. The question isn’t whether PWYW lowers your per-sale revenue—it does—but whether the volume increase offsets the loss.

    What buyers actually pay when you let them choose

    Three anonymised operators shared their Gumroad dashboard data after running PWYW experiments on digital products priced between $15 and $49. Here’s what happened:

    • Operator A: A $29 Notion template. Fixed price averaged $29 (obviously). PWYW with a $15 floor and $29 suggestion averaged $18.40 over 200 transactions. Conversion rate increased 22%, but total revenue dropped 18%.
    • Operator B: A $49 course bundle. PWYW with no floor and a $49 suggestion averaged $12.60 over 180 sales. Conversion rate doubled, revenue increased 9%. The operator considered it a win but switched back after realising most new buyers never purchased again.
    • Operator C: A $15 guide. PWYW with a $10 floor averaged $11.30. Conversion rate increased 8%, revenue dropped 31%. Switched back to fixed pricing within two weeks.

    The pattern: PWYW reliably increases conversion rate, but average order value collapses. The floor matters—Operator A’s $15 minimum kept the average above $18, while Operator B’s lack of a floor invited $5 and $7 payments on a product originally priced at $49.

    When PWYW makes sense (and when it doesn’t)

    PWYW works best as a short-term acquisition tool, not a permanent pricing strategy. The clearest use case is launching a new product to an untested audience. You’re optimising for feedback and social proof, not revenue. A $0 floor with a reasonable suggestion—say, $19 on a product you plan to price at $39—gets you early buyers, testimonials, and usage data you can fold into the final version.

    It also works for products with near-zero marginal cost and high repeat-purchase potential. If you’re selling a lightweight template or checklist and you have a clear upsell or backend offer, PWYW can fill the top of your funnel cheaply. But if the product is your only offer, or if you’re selling something that required weeks of work to build, PWYW usually just trains your audience to expect discounts.

    Where it fails: evergreen products with established audiences. If people already know your work and trust your pricing, PWYW doesn’t increase conversions meaningfully—it just gives existing buyers permission to pay less. Operator C’s 8% conversion lift didn’t justify a 31% revenue drop because the audience was already warm.

    The non-obvious cost: anchoring your own pricing

    The hidden risk of PWYW is psychological, not financial. Once you’ve let buyers pay $10 for something you later price at $39, you’ve anchored their perception of value. If they see the same product—or something similar—at the higher price later, they’ll assume you’re overcharging, not that they got a deal earlier.

    This is particularly painful if you run PWYW as a launch discount and then switch to fixed pricing. The buyers who paid $8 will tell others they paid $8. The new buyers who see $39 will Google your product, find Reddit threads or tweets mentioning the lower price, and wait for another sale. You’ve effectively turned a one-time experiment into a permanent discount expectation.

    The fix: if you use PWYW, frame it explicitly as a beta, a launch window, or a limited-time test. Set a public end date and stick to it. Don’t let it drift into your evergreen offer.

    Gumroad’s PWYW settings: what to toggle

    Gumroad gives you three levers: minimum price, suggested price, and whether to show the suggestion. The minimum is a hard floor—buyers can’t pay less. The suggested price is what Gumroad displays in the checkout field by default. If you hide the suggestion, the field is blank and buyers have to decide with no anchor.

    Showing the suggestion raises the average payment by 30–50% compared to hiding it, based on the data above. Operator B’s $12.60 average with a $49 suggestion would likely have dropped below $10 with no suggestion shown. If you’re going to use PWYW, always show a suggestion—and set it at 60–70% of your intended fixed price, not 100%. A $49 suggestion on a product you plan to sell for $49 just makes buyers feel like they’re being manipulated into paying full price anyway.

    The minimum should be at least 30–40% of your target price, unless you’re explicitly using PWYW as a free-plus-reputation play. Operator A’s $15 floor on a $29 product kept the average at $18.40. Operator B’s $0 floor tanked the average to $12.60 on a $49 product. Floors work.

    Want more pricing breakdowns, tool comparisons, and operator data? Subscribe to One Two Three Send and get one article like this every day.

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

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

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

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

    What admin-ajax.php actually does

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

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

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

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

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

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

    Common culprits include:

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

    Three ways to reduce the load

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

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

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

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

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

    When caching makes it worse

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

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

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

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

    One non-obvious tip

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

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

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