swing, thanks for the detailed confirmation earlier (the sentinel-file test) — that’s what let this get closed out properly. Follow-up here with the full picture on the welcome-tab side, after actually testing it (not just reading code) — sharing in case it helps someone else searching this thread later. Long post, but it’s all empirical now.
Correction on my part first: the tab is chrome://welcome/ (confirmed in brave-core source), displayed as brave://welcome in the address bar once loaded — not brave://welcome as I loosely called it earlier.
Where the pref you set (has_seen_brave_welcome_page) actually gets used: only to decide whether to redirect Japan-region users to a special variant of the welcome content (browser/ui/webui/brave_welcome_page/brave_welcome_page_ui.cc). It has nothing to do with whether the tab opens, and it gets set to true unconditionally the moment the page loads anyway.
The actual tab-opening decision (browser/ui/startup/brave_startup_tab_provider_impl.cc):
StartupTabs BraveStartupTabProviderImpl::GetDistributionFirstRunTabs(…) const {
StartupTabs tabs = StartupTabProviderImpl::GetDistributionFirstRunTabs(browser_creator);
if (first_run::IsChromeFirstRun()) {
tabs.emplace_back(GURL(kWelcomeURL));
}
return tabs;
}
This unconditionally appends the welcome tab whenever first_run::IsChromeFirstRun() is true — no pref consulted. That’s the exact same condition that gates whether initial_preferences gets read at all. So on current code, no command-line flag or pref decouples “skip the tab” from “read initial_preferences” — you get both or neither.
No admin policy purpose-built for this either — checked both Brave’s own policy set and upstream Chromium’s; nothing matches (closest upstream match, WelcomePageOnOSUpgradeEnabled, is deprecated/Windows-only/a different feature entirely).
I actually tested the one remaining theoretical option, Chromium’s generic URLBlocklist policy, rather than just reading about it:
First attempt failed silently — I used chrome://welcome/* (trailing wildcard), which loaded into the policy without any error, but did nothing: the page loaded normally. Turned out why, from reading url_matcher’s filter-parsing code (components/url_matcher/url_util.cc, FilterToComponents): the trailing /* is NOT treated as a wildcard for internal chrome:// URLs in this filter syntax — it’s parsed as a literal path-prefix string "/*", which a real navigation’s path (/) never matches. The policy UI’s own display of the saved filter even reflected this silently-mangled interpretation.
Corrected version — dropping the wildcard entirely (chrome://welcome and brave://welcome, no /*) — did work for a manual/typed navigation: navigating there in an already-running browser produced Chromium’s standard “This page is blocked — your organization doesn’t allow you to view this site” interstitial. So the policy mechanism itself is real and functional when the filter syntax is right.
But: testing the actual first-run scenario (fresh temp profile via --user-data-dir + --force-first-run, same machine-wide policy already active) — the automatically-opened welcome tab was not blocked; it loaded normally. Same policy, different code path/timing between a user-typed navigation and the browser-initiated first-run tab — I don’t know which, and I’m not going to guess. Didn’t chase it further.
Bottom line, now fully evidence-based rather than theoretical: there’s no clean way to suppress just the welcome tab while keeping initial_preferences processing, and the one policy-based workaround that exists (URLBlocklist) blocks the URL when you navigate to it manually, but not the automatic first-run tab specifically. Ignoring the tab (as you’re already doing) remains the practical answer.