DEV Community

Cover image for Query Strings vs. Hash Fragments: What’s the Real Difference?
Zahra Mirkazemi
Zahra Mirkazemi

Posted on

1

Query Strings vs. Hash Fragments: What’s the Real Difference?

Understanding URL Query Strings vs Hash Fragments

The other day, I stumbled upon something interesting. I was checking out a survey and noticed two URLs that looked like they pointed to the same place:

https://example.com/page?panel_view=true
Enter fullscreen mode Exit fullscreen mode

and

https://example.com/page#/?panel_view=true
Enter fullscreen mode Exit fullscreen mode

At a glance, you might think: "Same link, right?" Well… not exactly.

Let's dig into what's going on here — and why this small detail might actually matter, especially if you're working with modern web apps.

The Curious "#"

In the second URL, you'll notice a hash (#) followed by a slash:

#/?panel_view=true
Enter fullscreen mode Exit fullscreen mode

That little hashtag, also called a hash fragment, plays a special role in web development.

🔍 TL;DR:

  • Everything before the # = sent to the server
  • Everything after the # = stays in the browser

In other words: the server doesn't care about anything after the #. It never even sees it. But if your app is a single-page application (SPA) — built with something like React, Vue, or Angular — it might care a lot.

So, Why Use the Hash at All?

Back in the day, # was mostly used for scrolling to sections within a page (like #about-us).

Now, in modern SPAs, it's often used for routing. When you see something like #/dashboard or #/login, that's your front-end framework deciding what content to show — without refreshing the page.

Do They Work the Same?

They might! Or they might not.

If the app is a traditional server-rendered page, it likely uses query strings (?) to load different content.

If it's an SPA, the app might rely entirely on the hash (#/) to trigger internal views.

Using the wrong one could lead to weird issues — like skipping required steps, not loading the right view, or even breaking analytics.

What About Caching?

Great question. Here's where things get even more interesting:

🧠 Browsers and CDNs treat query strings and hash fragments very differently when it comes to caching.

  • Query strings (?panel_view=true) affect the full URL — including server-side caching and CDN behavior. So: Different query strings = different cached versions. This can be good (for dynamic content), but sometimes bad (if it busts the cache unnecessarily).

  • Hash fragments (#), on the other hand: Do not affect caching. The browser sees https://example.com/page#about and https://example.com/page#contact as the same resource — it just scrolls to a different part or changes state after the page loads.

💡 If you want to bust cache or serve different content from the server, use query strings. If you only need to affect front-end behavior, use hash fragments.

Catching These in Next.js

If you're using Next.js, here's how you grab each type:

✅ Query Strings

Next.js handles query strings natively using the useRouter hook:

import { useRouter } from 'next/router';

const Page = () => {
  const router = useRouter();
  const { panel_view } = router.query;

  return <p>panel_view is {panel_view}</p>;
};
Enter fullscreen mode Exit fullscreen mode

📦 Hash Fragments

Hash values aren't part of Next.js routing — they live only in the browser. You'll need to use window.location.hash:

import { useEffect, useState } from 'react';

const Page = () => {
  const [hash, setHash] = useState('');

  useEffect(() => {
    setHash(window.location.hash);
  }, []);

  return <p>Current hash is: {hash}</p>;
};
Enter fullscreen mode Exit fullscreen mode

Just remember: since this uses window, it must run client-side only (e.g., inside useEffect).

A Tiny Detail, A Bigger Lesson

This small difference is a great reminder that URLs are more than just links — they carry instructions. And depending on how your app is built, the format of those instructions can change what users see (or don't see).

Next time you copy-paste a link, give it a second look. That tiny # could be doing a lot more than you think. 😉

Ever been bitten by a query string or hash behavior in a project? Drop a comment — I'd love to hear your story!

AWS Security LIVE! Stream

Streaming live from AWS re:Inforce

Tune into Security LIVE! at re:Inforce for expert takes on modern security challenges.

Learn More

Top comments (0)

AWS Security LIVE! Stream

Streaming live from AWS re:Inforce

What’s next in cybersecurity? Find out live from re:Inforce on Security LIVE!

Learn More