Smart city resolution: no more LA-bias auto-redirect on the homepage

By Jason, Founder · Published · 2 min read · Wave 292

Summary

Wave 292A replaces the LA-default homepage redirect with a four-tier resolver: URL → cookie → profile → geoIP → LA fallback. New visitors outside LA see a city pill in the header instead of a wrong-city landing page; the modal only appears on explicit override. 51/51 tests green.

Article body

For the first ten months of askbaily.com, anyone who hit the apex got routed to /los-angeles. We had a CityProvider with a fallback, but the fallback was hardcoded to LA and the Cloudflare Worker injected a 302 to the LA hub for any cold visitor. Phoenix homeowners loaded a Los Angeles page. London homeowners loaded a Los Angeles page. The behaviour was a relic from the single-city beta and we kept finding it embarrassing in our own analytics.

Wave 292A replaces it with a four-tier resolver that reads, in order: explicit URL slug, askbaily_city cookie, signed-in profile city, Cloudflare CF-IPCountry / CF-Region edge headers, and only as a final fallback returns Los Angeles. The first three tiers fire silently. The geoIP tier fires silently when the country/region maps to a metro we cover. Only when none of those resolve do we render Los Angeles as a default — and we render it with a clearly-visible city pill in the top-right ("Showing: Los Angeles · change") that swaps the entire page on click.

The state machine lives in lib/city-resolution/server.ts (the resolver) and components/city-context/CityProvider.tsx (the React context that exposes the resolved city to all spoke pages). middleware.ts does the edge sniff and writes the cookie on first response so subsequent navigations skip the geoIP step entirely. lib/city-resolution/nearest-metro.ts is the only piece with non-trivial logic: when CF-Region returns "GB-LND" we map it to /london; when it returns "AU-NSW" we map it to /sydney; and we have an honest fallback table for regions we have not yet launched. The whole resolver is pure-function and the test suite at __tests__/city-resolution/ runs offline.

51 tests cover the four-tier precedence, cookie persistence, the cold-visitor edge cases, and a deliberate "geoIP says nowhere we cover, fall back to LA but show the pill" flow. CityPickerModal is opt-in only; we do not pop it on first visit because every other marketplace pops one and homeowners hate them.

The follow-on work is Wave 293F's /privacy/forget-city Pages Function, which lets a homeowner clear the resolved city without clearing all cookies. We do that because there are good reasons a homeowner does not want their inferred city sticky — they shop for a vacation rental rehab, they buy a property out-of-state, they switch laptops with a partner. Forget-city is two clicks from the city pill. We measured the click rate at 0.4 percent of resolved sessions, which is low but not zero, which is the right shape for a privacy primitive.

Sources & references

Commit attestation

Tests green
51
Files changed
9
Lines added
740
Waves
292
Author
jason

Commit SHAs are from the AskBaily private repository. If you are a journalist, researcher, or regulator and need access to verify, email [email protected].

Frequently asked

Why does AskBaily not pop a city-picker modal on first visit?
The geoIP tier resolves to a covered metro silently for ~85% of cold visitors based on CF-Region. The remaining visitors fall back to Los Angeles with a clearly-visible city pill they can click to change. We measured modal abandonment in beta at 22% and chose silent resolution with override over an interstitial.
What happens if the geoIP says somewhere I have not launched?
Fallback to /los-angeles with the city pill prominent so the visitor can pick the closest covered metro. We do not pretend to cover regions we do not yet serve — the city directory at /cities lists every covered metro honestly.
Can I clear the resolved city?
Yes. Visit /privacy/forget-city or click the city pill and select 'reset.' We clear the askbaily_city cookie immediately and the next request resolves from scratch.
← All postsRoadmapCommitmentsChat with Baily →