Flutter Web URL Strategies: Hash vs. Path Routing Done Right
Hash and path URL strategies in Flutter web, how to enable path URLs with flutter_web_plugins, the server rewrite you must not forget, and go_router interaction.
Published on • October 9, 2026
AI Assistant

Ship a Flutter app to the web and the address bar betrays your routing choice: example.com/#/products/42 or example.com/products/42. That single character decides your server configuration, shareability, and how deep links behave on refresh.
Flutter supports two URL strategies, and hash is still the default as of Flutter 3.47.
Hash vs. path
| Hash (default) | Path | |
|---|---|---|
| URL shape | site.com/#/products/42 | site.com/products/42 |
| Server config | None required | SPA rewrite to index.html required |
| Who sees the route | Only the client — the fragment never hits the server | The server sees every path |
| Shareability | Ugly, fragments often stripped by chat apps | Clean, canonical |
| Refresh / deep link | Always works | Breaks without the rewrite |
Hash URLs are robust precisely because the server never participates: anything can host them — S3 buckets, shared hosting, even file://. Path URLs look like a conventional web app but shift responsibility onto your hosting configuration.
Enabling path strategy
Call usePathUrlStrategy() before runApp():
import 'package:flutter_web_plugins/url_strategy.dart';
void main() {
usePathUrlStrategy();
runApp(const ExampleApp());
}
flutter_web_plugins cannot be pub add’d — declare it as an SDK dependency:
dependencies:
flutter:
sdk: flutter
flutter_web_plugins:
sdk: flutter
Under the hood this selects PathUrlStrategy, which reads platformLocation.pathname and respects your <base href>. The lower-level API is setUrlStrategy(PathUrlStrategy()) or setUrlStrategy(const HashUrlStrategy()) — hash is the engine default, so doing nothing gives you #/.
The server rewrite you must not forget
PathUrlStrategy uses the browser History API. Every unmatched request must fall back to index.html, or refreshing a deep link returns a 404 from your server even though the app routes fine in-session.
nginx:
location / {
try_files $uri $uri/ /index.html;
}
Firebase Hosting — choose “Configure as a single-page app” when initializing, which writes:
{ "rewrites": [{ "source": "**", "destination": "/index.html" }] }
flutter run -d chrome already falls back to index.html gracefully, which is exactly why the bug usually appears for the first time in production.
Sub-path hosting and base href
If the app is not deployed at the domain root, edit <base href> in web/index.html to match — for example /flutter_app/. Relative base href values (".", "..") work for release builds but differ per request path, so pin it explicitly for anything non-root.
Interaction with go_router
go_router reads whatever path Flutter reports — it does not configure the strategy for you. Configure the strategy in main.dart independently of your router. Note there is no official useHashUrlStrategy() in flutter_web_plugins; the old url_strategy package from pub.dev that provided those helpers is now marked discontinued and should not enter new projects.
go_router’s web topic links to the Flutter URL strategy page for exactly this reason: router choice and URL strategy are orthogonal decisions.
When to choose which
Choose hash when:
- You do not control server config (S3, static buckets, GitHub Pages without redirects)
- It is an internal tool where URL aesthetics do not matter
- You want zero-configuration maximum compatibility
Choose path when:
- It is a public, shareable product — canonical URLs matter
- You control rewrite configuration and can test refresh + deep links
- You want links that survive being pasted into chat apps without
#mangling
One honest caveat: Flutter’s docs do not make strong SEO claims either way — modern Googlebot renders JavaScript. The real argument for path URLs is shareability, canonical structure, and server-side control (caching rules, redirects, analytics that see full paths).
Gotchas
- Path strategy without the rewrite = first refresh in production 404s.
- Strategy must be set before
runApp(). <base href>must match the deployment subpath.- Hash fragments are never sent to servers — you cannot canonicalize or server-render hash routes.
- Do not add the discontinued
url_strategypackage;flutter_web_pluginscovers it.
Wrapping up
Pick hash for zero-config robustness, path for a production web presence — and if you pick path, write the SPA rewrite in the same pull request that enables usePathUrlStrategy(). Then verify by refreshing a deep link on a staging deploy before you ship.
References
- Configuring the URL strategy on the web - official Flutter docs for hash vs. path
- flutter_web_plugins url_strategy API -
PathUrlStrategy,HashUrlStrategy,setUrlStrategy - go_router web topic - router interaction with web URLs