Default registration country from Cloudflare IP header
What does this MR do and why?
The registration welcome form's country/region selector starts empty, so every new user has to open the dropdown and pick a country. This is a required interaction on the highest-traffic step of signup.
GitLab.com already receives the visitor's country on every request.
Two independent Cloudflare settings each send Cf-Ipcountry, and
either alone is sufficient: the add_visitor_location_headers
managed transform,
which is enabled for the gitlab.com zone in
gitlab-com/gl-infra/config-mgmt, and the zone's IP Geolocation
network setting, which Cloudflare documents as sending the header
"even when Add visitor location headers is turned off".
GitLab already consumes this header for the unknown-sign-in
notification email and for PIPL compliance checks, and logs it as
cf_ipcountry on every controller request.
This MR uses that header to pre-select the country in the welcome form. The field stays editable, so a wrong guess only costs the user a correction rather than producing bad data.
Behind the default_registration_country_from_ip feature flag,
default disabled.
Implementation notes
- Adds a
country_codemethod as an EE extension inee/lib/ee/gitlab/auth/visitor_location.rb, injected viaprepend_modonlib/gitlab/auth/visitor_location.rb. It's deliberately EE-only because reading visitor location is tracking-adjacent. The CE-side change is a singleprepend_modline. country_codereturns the raw ISO 3166-1 alpha-2 code, andnilwhen the header is absent, blank, or one of Cloudflare's unresolved sentinels:XX(could not resolve the IP) andT1(Tor exit node).ee/app/components/registrations/welcome/form_component.rbgains adefault_countrymethod used for thecountrykey in the view model. Precedence: a submittedparams[:country]wins (so a failed submit round-trips the user's own choice), then the detected code, then an empty string.- The detected code is filtered through
World.countries_for_select. The selector omits ten countries (World::COUNTRY_DENYLISTplus theJH_MARKETentries), so a detected country with no matching option falls back to empty rather than an unselectable value. - Self-managed instances have no Cloudflare in front of them, so the header is absent and the field behaves exactly as it does today.
- No frontend or controller changes needed:
ViewComponent::Basealready exposesrequest, andfree_welcome_form.vuealready readsuserData.countryinto its form values. user_datawas already at theMetrics/CyclomaticComplexitylimit, which is why this logic is extracted intodefault_countryrather than inlined.
How to set up and validate locally
The header isn't present in GDK, so it has to be supplied manually.
- Enable the flag in the Rails console:
Feature.enable(:default_registration_country_from_ip) - Request the welcome page with the header set, with a signed-in
session, e.g.
curl -H 'Cf-Ipcountry: NL' <gdk-url>/users/sign_up/welcome(or add the header via a browser extension) - The country selector should render with the matching country pre-selected.
- Try
XXandCNto confirm both fall back to an empty selector.
Related
Measuring how often the header resolves to a usable country, and how often users correct the pre-filled value, is not covered by this MR and needs separate instrumentation. On GitLab.com the submitted country is only passed through to CustomersDot rather than persisted on the user.