In a microfrontend shell application, what is the difference between path-based routing (e.g. example.com/checkout) and hash-based routing (e.g. example.com/#/checkout) for deciding which microfrontend to load?
answer
- # never hits the server
- SPA fallback / catch-all rewrite
- pathname vs hash listener
- SEO + SSR need real paths
- ownership enforceable at CDN only for path routing
basics
~20 sPath-based routing uses the real URL path and needs server/CDN config to serve the app for every path; hash-based routing puts the route after a # so the browser never asks the server for it, but it looks less clean and hurts SEO.
solid answer
~40 sPath routing (`/checkout/cart`) treats the segment after the domain as the real address; the web server or CDN must be configured to return the shell's `index.html` for any such path (a catch-all/rewrite rule), after which client-side JS reads `window.location.pathname` to decide which microfrontend to mount. Hash routing (`/#/checkout/cart`) exploits the fact that browsers never send the fragment after `#` to the server, so any static host works with zero rewrite rules, but URLs are uglier, non-SEO-friendly, and don't work with server-side rendering per route. Most production shells (single-spa, Module Federation shells) default to path-based routing with proper server rewrites and reserve hash-based for embedded/legacy or iframe-constrained contexts.
go deeper
Should correctly state that hash routing needs no server config while path routing does, and give the syntax difference. Doesn't need to discuss ownership or SSR implications.
Should explain the SPA-fallback rewrite mechanism and name at least one concrete trade-off on each side (SEO/SSR for path; zero-config embeddability for hash).
Should connect the choice to ownership enforcement — path prefixes can be routed at the CDN/edge to different origins per team; hash routing can't be enforced below the client JS layer.
Should reason about this as a platform decision with organizational consequences: which choice lets teams deploy independently, be indexed independently, and enforce boundaries without trusting every other team's JS.
## What the shell is deciding A **microfrontend shell** (also called an app-shell, container, or root config) is the top-level application responsible for deciding, based on the current URL, which independently-built and independently-deployed microfrontend to fetch and mount into the page. That decision is made by a router living in the shell, and the two most common strategies for encoding "where am I" in the URL are **path-based routing** and **hash-based routing**. ## Path-based routing Path-based routing uses the actual URL path segment — `https://example.com/checkout/cart` — as the routing key. The shell's router (often a thin wrapper around the History API: `pushState`, `replaceState`, `popstate`) inspects `window.location.pathname`, matches it against a route table such as `/checkout/* -> checkout-mfe`, `/account/* -> account-mfe`, and mounts the matching bundle. Because the path is a real URL, every navigation has to resolve to something the web server or CDN can serve: - a `pushState` from an in-app link - a full page load - a bookmarked URL - a server-rendered link from an email That means the server (or edge/CDN rewrite rule) must be configured with a catch-all rule that returns the shell's `index.html` (or an SSR response) for any path that belongs to the app, rather than 404ing, because the actual routing decision happens client-side after the shell's JS boots. This is the same "SPA fallback" problem every single-page app has, just centered in the shell rather than one app. ## Hash-based routing Hash-based routing instead encodes the route after a `#`, e.g. `https://example.com/#/checkout/cart`. The browser treats everything after `#` as a same-document fragment identifier and — critically — **never sends it to the server** as part of the HTTP request. The server only ever sees a request for `/`. This means hash routing needs zero server-side rewrite configuration: any static file host, any CDN, any environment that can serve a single `index.html` works out of the box, because the server is oblivious to what's after the `#`. The shell's router instead listens to the `hashchange` event (or intercepts clicks and manually parses `location.hash`) to decide what to mount. ## The trade-off The trade-off is real and multi-dimensional. Path-based URLs: - are clean, and match user expectations of "real" URLs - are indexable by search engines and social-media link previews - support proper server-side rendering per route (the server can inspect the real path and render the right content before JS loads) Their cost is operational: every environment the app runs in — production CDN, preview deployments, local dev proxy, an embedding host if the shell is iframed — needs the SPA-fallback rewrite rule configured correctly, and misconfiguring it produces hard-to-debug 404s on deep-link refresh or on cold navigation from an external link. Hash-based URLs: - need no such configuration - are trivially embeddable (e.g., a microfrontend embedded inside a third-party CMS page where you don't control server routing at all) - but they are non-SEO-friendly and look unpolished to end users - and make server-side rendering per-route effectively impossible since the server never sees the fragment ## Ownership at the infrastructure layer In a microfrontend context specifically, this choice compounds with ownership: path-based routing lets you cleanly map a URL prefix to a team's deployable unit (`/checkout/*` is unambiguously "the checkout team's territory," enforceable at the CDN/reverse-proxy layer with routing rules that can even route different path prefixes to entirely different origins/servers — true edge-level microfrontend isolation). Hash-based routing collapses everything under one served document, so ownership boundaries can only be enforced in client-side JS, not at the infrastructure layer — any microfrontend's JS could in principle read or rewrite another's hash-route, and there's no server-side way to say "this MFE serves this business path." ## What mature platforms actually do In practice, most mature microfrontend platforms (single-spa's root-config, module-federation-based shells) default to path-based routing because it aligns URLs with business capability boundaries and supports SEO/SSR, accepting the operational cost of getting rewrite rules right in every environment. Hash-based routing survives mainly in legacy migrations, iframe-embedded widgets, or contexts where the team genuinely doesn't control server/CDN configuration.
- Why does hash-based routing avoid the need for any server-side rewrite configuration?The browser never sends the fragment after `#` in the HTTP request line — the server only ever sees a request for the base document (e.g. `/`), so there's nothing for it to 404 on. The shell's JS reads `location.hash` client-side after the single document has already loaded, so no server route table needs to know about individual microfrontend paths.
- What breaks if a CDN's SPA-fallback rewrite rule for a path-based shell is misconfigured?Deep links and hard refreshes on any route other than `/` return a real 404 from the server instead of the shell's index.html, because the server tries to resolve the path as a literal resource before the client-side router ever gets a chance to run. This typically shows up as 'the app works when I click around but breaks on refresh or when someone shares a link.'
- Can you mix path-based and hash-based routing within the same microfrontend shell?Yes, though it's unusual and adds complexity: some platforms use path-based routing for the primary business routes (for SEO/SSR) and hash-based sub-routing inside a single mounted microfrontend for ephemeral UI state (a modal step, a tab) that shouldn't be a server-indexable route of its own.
Path-based routing is like a building with real street addresses each department can put on its own door and have mail delivered directly; hash-based routing is like everyone sharing one lobby address and using a handwritten note pinned inside the door to say which department you actually want.
saying these in an interview costs you the question
- Says hash routing is purely 'the old way' with no operational trade-off reasoning
- Doesn't mention the SPA-fallback/catch-all server rewrite requirement for path routing
- Thinks the server 'sees' and can route on the hash fragment
- Can't explain why hash routing needs no server config
- Ignores the SEO/SSR implications entirely