URL fragments and the hash
The part of a URL after the # is called the fragment, and it behaves differently from everything before it. The single most important fact about it surprises a lot of developers: the fragment is never sent to the server. Knowing that explains where it is useful and where it will quietly let you down.
The fragment never reaches the server
When a browser requests a URL, it strips the fragment before sending. The server sees the path and the query string, but not the # or anything after it.
https://example.com/docs?lang=en#installation
│ │ │
path query fragment (stays in the browser)
For that URL the server receives /docs?lang=en. The installation fragment stays entirely on the client. This is defined behavior, not a quirk, and it is why the fragment cannot be read in server logs or used in server side routing.
What it was made for
The original job of the fragment is to point at a location within a document. If an element on the page has a matching id, the browser scrolls to it after loading.
<h2 id="installation">Installation</h2>
Visiting #installation jumps straight to that heading. This is the basis of anchor links and table of contents navigation, and it works with no JavaScript at all.
Fragments in single page applications
Because changing the fragment does not trigger a server request and does not reload the page, SPAs adopted it for client side routing. The app listens for fragment changes and swaps views in response.
window.addEventListener("hashchange", () => {
render(window.location.hash); // route entirely in the browser
});
This is the older hash based routing style, seen as /#/dashboard. Modern apps often use the History API for cleaner paths, but hash routing still works everywhere and never needs server configuration, which makes it handy for static hosting and deep links into an app state.
How it differs from the query string
Fragments and query strings look similar but serve opposite sides of the request.
Query string (?) | Fragment (#) | |
|---|---|---|
| Sent to server | Yes | No |
| Read by server code | Yes | No |
| Changes without reload | No (normally) | Yes |
| Typical use | filtering, parameters | in-page anchor, client routing |
The practical rule follows directly: if a value must reach the server, put it in the query string or the path, never the fragment. The fragment is the right place for state that is purely the client's concern, such as which tab is open or which heading to scroll to. Sensitive data also does not belong in either, but the fragment staying client side is not a security feature, since it still sits in the browser history and can be read by any script on the page.
Inspect it yourself
To see exactly how a URL splits into path, query, and fragment, the URL Tools break any URL into its parts in your browser, so the URL never leaves your device. It makes the boundary between what the server sees and what it does not immediately clear. For the full breakdown of every component, see parse a URL into its parts.
The takeaway: the fragment is client only. Use it for in-page anchors and client side state, keep anything the server needs in the query string, and never assume a fragment will show up in a log or a backend handler.