Build1 publisher2 min readPublished
A CDN country header served Googlebot $14.99 on a page declaring GBP in its structured data
A dev.to writeup traces wrong-currency indexing to two code paths computing the same fact, and fixes it by resolving the currency once and handing the same value to the rendered price and the JSON-LD.
The Engineer · Build desk

What happened
- A landing page that localises price from a CDN country header served $14.99 to a crawler calling from a US datacentre, so Google indexed a business that charges in pounds as an American product.
- The Product structured data was generated on a separate path and still declared GBP, leaving the rendered price and the declared price in disagreement on the same page.
- The fix is a single branch at the top of the currency resolver: matching bot user agents get BASE_CURRENCY, and everything else is geolocated from the x-vercel-ip-country header.
- The bot pattern also lists the chat unfurlers, because without them a pricing link pasted in a London office can generate a preview card priced in Brazilian real from wherever the scraper runs.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- constraint One stable answer for crawlers only stays truthful while the base currency is the currency payments settle in. If a business bills US customers in dollars and UK customers in pounds, the single figure every crawler gets stops describing its pricing.
- exposure The correctness of the branch depends on other companies' user-agent strings. The entries for the platform's internal fetches and for the five unfurlers have to track whatever those firms send next, and the site stays quiet when one of them changes.
- decision Every surface that quotes a price now has to take the currency as an argument instead of hardcoding it. Teams that compute currency in the template and again in the JSON-LD builder are maintaining two answers to one question.
The branch is four lines. `getDisplayCurrency` awaits `headers()`, tests the user agent, and returns `BASE_CURRENCY` when it matches; everything else falls through to `currencyForCountry(headerList.get('x-vercel-ip-country'))` [5]. BASE_CURRENCY is GBP, which the author says is the currency the subscription is genuinely priced in and the one the structured data declares [6]. A crawler reads the same figure from any datacentre.
Where it breaks is the test itself. `isBotUserAgent` returns false when the user agent is null or undefined [7], so a client that sends no user agent takes the geo path and gets whatever country its exit IP resolves to [18].
One failure direction costs more than the other. A false match shows a human in Frankfurt the GBP price, and the author already describes the localised figure as an approximation [15]. A miss restores the original defect, where the rendered price comes from the datacentre's country and disagrees with the GBP in the `Product` markup [3][19]. Over-matching is the cheap error. A pattern that fires on any user agent containing the letters "bot" is defensible here [7].
The author writes: "The structured data mismatch is not really a crawler problem, it is a duplication problem." [11] The page component resolves the currency once and hands it to the JSON-LD graph, the rendered pricing section and `faqs(currency)` [12]. That last call matters. The FAQ contains the sentence "£10 a month for unlimited scanning", and that sentence is also emitted as `FAQPage` structured data [13].
The detector sits in its own module because two callers need the identical answer, the currency resolver and the edge proxy [10]. "Two regexes that are supposed to agree will eventually disagree," the author writes [10].
What could cost the most here is documented the least. According to the post, a price in structured data that does not match the price on the page is a documented reason for rich results to be suppressed, and in worse cases a manual action instead of a silent demotion [4]. The post does not cite that documentation or report any action taken against the site [21]. What it does report is the observed bug it opens with: Googlebot indexing a business that charges in pounds as an American product at an American price [2].
You can check the fix from outside. On the published page, a curl with a bot user agent returns the same thing every time, while a VPN to Germany or Japan changes what a browser shows [16].
What to watch
- Whether the site reports recovered Product rich results in Search Console now that the rendered price and the markup agree.
- Whether the currency stays a parameter as more surfaces start quoting the price: transactional email, checkout, OG images.
- Whether any other team publishes measured impression or rich-result data for this pattern, since the account so far is one site's own report.