I launched BorderFolio on August 31. Twenty-five days later Google was testing several of its pages on the first page of results.
Not for portfolio tracker. For much smaller and much more relevant searches — spy irish domiciled at position 9, ireland domiciled qqq at 9, depotauszug trade republic at 7, and one very specific query about a fund's ongoing charge at position 6.
That's a tiny dataset and I'm not claiming I won SEO in 25 days. But it was enough to answer the only question worth asking that early: had Google worked out what this site was about? It had.
The thing that got me there wasn't publishing more. It was that I stopped treating SEO as a publishing pipeline and started treating it as a control loop.
That reframe is the whole post. The rest is how it's wired.
1. The loop
Most SEO advice describes an open loop. You publish, you hope, you publish again. There's no feedback term, so there's nothing to tune:
publish → publish → change ten titles → publish → panic
What I run now:
Search Console → signal → ONE change → wait → measure → repeat
The important part isn't the arrows. It's that exactly one change is in flight at a time, and that the loop is allowed to output no change at all.
If you edit a title, add internal links and rewrite the intro in the same week, and the position moves, you've learned nothing. Three experiments, one subject, no control. Every engineer knows this and almost nobody applies it to their own marketing site.
2. Build pages around decisions, not keywords
Before the loop can tell you anything, there has to be something for Google to classify.
BorderFolio is portfolio analytics for people who live in one country and hold funds domiciled in another. That gives a specific search surface: ETF domicile, dividend withholding, US estate-tax exposure, UCITS alternatives, broker statement imports.
I didn't target portfolio tracker. I targeted what someone types while actually stuck:
- What's the Irish-domiciled equivalent of QQQ?
- Is VWRA better than VT for a non-US investor?
- What's an Irish-domiciled alternative to SPY?
- Can I import my Interactive Brokers statement into a tracker?
Each page answers one decision. That matters for the loop specifically: a page answering one decision produces a clean signal. Google either matches it to that intent or it doesn't, and you can read the result. A 2,000-word page covering six topics produces mush that tells you nothing when it moves.
3. Cluster, don't scatter
I didn't want Google to see 100 unrelated pages, so pages link to each other in a deliberate shape:
US ETFs vs Irish UCITS ← concept page
│
├── QQQ · SPY · VOO · VT · SCHD ← per-fund alternatives
│ │
│ └── QQQ vs CNDX, VOO vs CSPX ← head-to-head
│
└── withholding by residence · estate tax ← the tax layer
Hub links down, leaves link up and sideways, and everything links into the tax layer it depends on. It isn't clever, but it's the difference between 100 pages and one topic.
4. Fix the boring things first
Before backlinks, before publishing more, three unglamorous problems.
The sitemap, and an honest caveat. ~141 URLs submitted, 0 errors, 0 warnings. Worth being precise about what that report means: 141 submitted is not 141 indexed. Search Console's sitemap panel tells you what you handed over, not what Google decided to keep. Treating submission as indexation is the easiest way to think you're fine when you aren't.
Auth pages were getting indexed. app.borderfolio.app/login had picked up impressions at an average position around 9 — search traffic that can only bounce.
The fix is noindex, but the important detail is what not to do:
# WRONG — Google can't see a noindex on a page it isn't allowed to fetch
User-agent: *
Disallow: /login
A Disallow hides the noindex. The URL then sits in the index with no snippet instead of dropping out. So the auth pages stay crawlable on purpose and the directive goes in the response:
# the app host is the product, not a content surface
add_header X-Robots-Tag "noindex, follow" always;
A header rather than a per-page <meta> because it covers routes rendered client-side too. follow, not nofollow, so links back out still carry. Once Search Console shows them gone, then you can add the Disallow and stop the crawl as well.
Duplicate URL variants. Google had found both borderfolio.app/privacy.html and www.borderfolio.app/privacy for one document. Standardising on the apex host fixed half of it; the other half was in the server config:
# this served /terms AND /terms.html as two live 200s
location / {
try_files $uri $uri.html $uri/ =404;
}
That $uri.html in the chain is the duplicate. Drop it, redirect instead, and every document has exactly one URL that answers 200.
One trap while doing this: a location ~ index\.html that redirects /index.html → / also catches nginx's internal rewrite of / and loops forever. Match on $request_uri instead — it keeps the request as the client actually sent it.
None of this is growth hacking. It's making sure the thing you measure is the thing you shipped.
5. Wire Search Console into the loop
This was the change that actually altered how I work.
I connected the Search Console property to an MCP-compatible data source (via Windsor.ai) so I can query it directly instead of clicking around the UI. The fields that matter:
date · page · query · clicks · impressions · ctr · position
The UI pushes you toward "what should I write next?" because that's the shape of the interface — a blank content calendar. A queryable dataset lets you ask better questions:
- Which pages did Google start testing this week?
- Which pages sit at position 5–15 with a 0% CTR?
- Which queries has Google actually attached to my QQQ page — not the ones I hoped for?
That last one changes decisions. Google tells you how it classified your page, which beats any keyword tool's opinion about what the page is for.
I run the analysis on a schedule. The rule is that it doesn't have to recommend something. A loop that must emit an action every tick isn't a control loop, it's a random walk. This has been surprisingly hard to stick to.
6. What Google actually revealed
Search Console anonymises low-volume queries, so per-page impression totals run much higher than the sum of the visible query rows. On one two-day window it exposed exactly two query strings — which didn't mean two searches, just that the rest were below the privacy threshold.
The ones it did show were more useful than any volume estimate:
| Query | Position |
|---|---|
spy irish domiciled |
~9 |
schd irish domiciled |
~9.3 |
ireland domiciled qqq |
~9 |
depotauszug trade republic |
~7 |
| a very specific query about SPYL's ongoing charge | ~6 |
qqq equivalent in ireland |
~12.5 |
ibkr portfolio tracker |
~13.5 |
vwra vs vt |
~19, with one organic click |
Two things fall straight out of that table.
Google understood the site. Those are the queries the pages were built for. At 25 days old that was the only thing I was trying to measure — not traffic, just whether the classification landed.
qqq equivalent in ireland is a free title. I don't have to guess the phrasing; Google just told me. A vague title about Nasdaq ETFs becomes:
QQQ Irish-Domiciled Equivalent: CNDX vs EQQQ
Same page, same content, language taken from observed behaviour instead of invented. That's the kind of optimisation I now want — not "best practices," but a response to something measured.
7. The best-performing page wasn't a landing page
I wrote an article called VWRA vs VT: why the choice isn't just about TER. The premise: every comparison of those two funds collapses into "VT has a lower expense ratio," and for a non-US investor the expense ratio is nowhere near the biggest term.
Within days Google was testing it around position 5 on a handful of impressions.
I did not touch it.
That's now a rule: if Google is moving a new page from nowhere into positions 5–10, don't immediately optimise it because the sample looks small. Let it finish testing. Editing mid-test destroys the measurement — you'll never know whether your change helped or whether it would have settled there anyway.
8. The mistake: 242 impressions that meant nothing
I also built country-specific withholding-tax pages. Some pulled real impression counts:
| Page | Impressions | Position |
|---|---|---|
| withholding tax slovakia | ~242 | ~55 |
| withholding tax ukraine | ~217 | ~54 |
| withholding tax czech republic | ~186 | ~65 |
| withholding tax hungary | ~175 | ~66 |
Hundreds of impressions on a three-week-old site looks like a win for about ten seconds — until you read the second column.
Position 55 isn't "we're ranking." It's Google saying I tested whether this page belongs in a much broader query, and it didn't. The intent was wrong too: someone searching withholding tax slovakia is probably not a cross-border investor asking about US dividend withholding at their residence.
242 impressions at position 55 taught me less than 20 at position 6.
The obvious reaction was to generate another 100 country pages. I didn't. I put the effort into the cluster where Google was already handing me first-page exposure, because that signal was clean and the intent matched the product.
Impressions are the vanity metric of SEO. Relevant impressions at a competitive position are the real one.
9. The rules that came out of it
Effectively the loop's control policy:
Position 6–9, impressions growing, CTR 0% → title experiment. The page earned the placement; it's the SERP listing that's failing.
Position 40–70 → don't touch the title. A CTR fix on a page nobody sees is noise. Either the page isn't competitive or the intent is wrong, and both are bigger problems than copy.
Brand-new page already at position 5 → do nothing. Google is still testing.
That last rule is the hardest and has paid best. A new page at position 5 on five impressions creates enormous pressure to optimise it. Optimising it is how you lose the result.
10. What I'm deliberately not doing
- Hundreds more country pages
- Daily title changes
- Generic high-volume finance articles
- Mass AI-generated content expansion
- Rewriting pages Google is already testing successfully
Doing less is a property of the loop, not a lack of ambition. Every change made while another is in flight costs you the ability to attribute either.
I did add one more page recently, on the VTI Irish-domiciled-alternative question. I'm not judging it until Search Console shows impressions or queries for it.
11. Where this goes
25 days is nothing. Clicks are tiny, most samples are single digits, and some of these positions will evaporate. I'll write the follow-up when there's enough data to say whether they held.
But the process changed from guessing to engineering. There's telemetry, hypotheses, one experiment at a time, and a measurement window. Instead of maybe Google will like this article, the question became:
Google put this page at position 7. What exactly was it testing, and what single change would move it?
That one has an answer.
If your SEO currently consists of publishing and hoping: the loop costs nothing to start. Search Console already has the data. The only thing you have to add is the discipline to change one thing at a time — and to accept "do nothing today" as a real output.
I'm building BorderFolio, portfolio analytics for investors dealing with ETF domicile, withholding tax and brokers in more than one country. This documents the process as I build it in public. Originally published on Medium.
Top comments (2)
"One change in flight at a time" is just controlled experimentation applied to SEO, and it's rare to see someone actually stick to that instead of changing five things and calling it a win. The Disallow vs noindex header mix up for the login pages is a subtle trap a lot of people fall into. Blocking the crawl before the deindex means Google never even sees the noindex directive.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.