SEO

Are URL Fragments (Hash) Indexed by Google? SPA and # Explained

Talha Aslan 18 min read 3 views

Are URL fragments (hash) indexed by Google?

No. A URL fragment, the part after the # sign, does not become a separate indexed page in Google. Google's documentation says Google Search generally does not support URL fragments. However, a fragment that jumps to a spot on the same page is harmless. A fragment that swaps the page content is a problem.

That short answer hides a few distinctions, so read on. Also, not every site faces the same situation. This guide takes one question and answers it fully: what happens to content behind a URL fragment, what you do in a single page application, and where jump links and UTM tags fit.

We are Talha Aslan and team, and we usually meet this issue when a new site launches and "half of our pages are missing from Google." The cause is often the way the site handles navigation.

What is a URL fragment and what does the browser do with it?

A fragment is the part of an address that starts with a # sign. For example, in example.com/guide#pricing, the word "pricing" is the fragment. The browser loads the page, then scrolls to the element with the matching ID.

Here is the detail that matters: the browser normally does not send the fragment to the server. So the server sees no difference between "guide" and "guide#pricing". So the fragment lives entirely in the browser.

That behavior has two consequences:

  • The server cannot return different content for a hash, because it never learns the hash.
  • Any change of content has to come from JavaScript, the code that runs in the browser.

So "the content changes with the hash" really means "a script swaps the content." A search engine expects one response per address. That mismatch between the expectation and a hash based design is the root of the problem.

Why does Google not treat the URL fragment as a separate page?

Google's URL structure documentation says it directly. Do not use fragments to change the content of a page, because Google Search generally does not support URL fragments. The bad example in that document shows a path that starts with #/ inside the address.

The logic is simple, because Google works address by address. Google requests an address, receives a response, and ties that response to the address. The fragment never reaches the server, so example.com/#/products and example.com/#/contact return the same response. Then, to Google, they are one page.

You can safely say this: Google generally does not treat fragment separated addresses as separate pages. The word "generally" comes from the documentation itself. We do not promise more than that, but it is safer to design for this assumption than to hope for the opposite.

The practical result: if a view has search value, give it a real URL. Also, views separated only by a fragment cannot appear in results with their own title and description.

What happens on a site that changes content with the URL fragment?

Consider an example scenario. A product site manages its whole menu with hashes: home is example.com/#/, products is example.com/#/products, and pricing is example.com/#/pricing. At first, everything works in the browser.

However, when Google crawls this site, it sees only example.com. The menu links point to fragment addresses, so Google does not discover separate pages. As a result, only the home page shows up in search. Then your pricing view never appears for any query.

On a site run with a URL fragment, you lose three things:

  • Discovery: Inner views are not found as separate pages.
  • Signals: Also, external links often point to the hash address and count toward the home page.
  • Measurement: Analytics shows all visits as one page.

For example, this is an architecture scenario, not a customer result. Still, we see the pattern often, especially on corporate sites built with older JavaScript frameworks.

How do you set up correct routing in a single page application?

If you run a single page application (SPA, a web app that switches views without reloading the page), the answer is the History API. Google's JavaScript SEO basics documentation recommends the History API for routing between the views of your app.

The History API lets you update the address bar without a reload. When a user opens the pricing view, the address becomes example.com/pricing. Also, that address is a real path, and the server can answer it too.

A correct setup has these parts:

  1. Each view has its own clean, permanent path.
  2. If you type that path directly into the browser, the server returns the same content.
  3. Menu links are real link elements, and the href value carries the path.
  4. Each view has its own title and description.

Also, server side rendering or pre-rendering, which means preparing the content as HTML in advance, makes it easier for Google to see the content. So that choice depends on your performance and maintenance goals. Whether it is required depends on your site's structure.

Does Google still support hashbang (#!) URLs?

A hashbang is an old format where an address starts with #! characters. At one time, Google described a special "AJAX crawling scheme" to handle those addresses. However, that scheme is now deprecated.

Google Search Central's official account has stated that the hashbang and AJAX crawling scheme were deprecated a long time ago. It recommends moving to a cleaner URL structure that does not need # or #!. So a new project has no reason to adopt hashbangs. A fragment is a fragment, and #! does not create a separate page either.

If you find #! on an older site, do the following:

  • Accept that the scheme is no longer supported and stop relying on it.
  • Then define a real path for every #! address.
  • Plan a migration from the old addresses to the new paths.

Note: The server never sees the hash, so you cannot redirect a hash address directly with a 301. This limit needs its own solution, which we cover in the migration section below.

Are jump links with # a problem?

No. Using # for on-page jump links is fine. In that use, the hash does not change the content. Instead, it only scrolls to a heading or section on the same page.

A table of contents at the top of a long guide is a good example. The "Pricing" link goes to the pricing heading on the page. The content is already there, so Google does not need to see a separate URL.

This also helps usability. For example, readers reach the part they want in one click. In addition, keyboard and screen reader users navigate faster.

There is only one thing to check: the target ID must exist on the page, and the content must be in the HTML on first load. If the ID is created later by JavaScript, the jump may fail.

Whether those jump links appear as "Jump to" style links in search results is a separate question. We covered it in our post on table of contents and jump to links in Google, so we do not repeat it here.

Should tabs, accordions, and filters use the URL fragment?

Not if the view has search value, because the content must be findable. When the text inside a tab or accordion already sits in the page HTML, the hash only tells which one is open. In that case, the content also stays visible. Trouble starts when content loads only after the hash changes.

You can use this split:

  • Visual state: Which tab is open can live in the hash.
  • Separate content: If each tab should be a searchable page, give it a real URL.
  • Filters: You can build filtered lists with a query string (like ?color=blue) instead of a hash, but many filter combinations call for an indexing plan.

We covered the problems of filter combinations in our post on faceted navigation. Escaping to a hash does not solve that problem. Instead, it only hides it.

In short, if you show the user a setting, a hash is enough. For everything you want Google to find, you need a real address.

What happens if you put UTM parameters after the #?

If you put UTM tags after the #, your analytics data may break. The Google Analytics Help article on URL builders tells you to separate the parameters from the URL with a question mark. It also says to write parameters as name and value pairs joined by an ampersand.

The reason is easy to see, because the hash stays in the browser. The hash does not go to the server, and many setups read only the query string. If your tag stays after the #, the campaign source can land in the wrong group, such as "direct" or "referral."

This behavior can differ between tools. So test it in your own setup. Also check the official help of your ad platform to see how it handles a fragment in the destination URL.

The safe rule: query string first, hash last. For example, example.com/page?utm_source=newsletter#pricing is correct. Instead of typing tags by hand, use our UTM builder. For the full logic, read what UTM parameters are.

How do a URL fragment, a query string, and the History API differ?

However, the three methods do not serve the same goal. The URL fragment stays in the browser, the query string goes to the server, and the History API updates a real path in the browser. The table shows them side by side.

MethodDoes the server see it?Separate page for Google?Best use
------------
Fragment (#section)NoGenerally noOn-page jumps, small interface state
Hashbang (#!)NoNo, the scheme is deprecatedDo not use
Query string (?page=2)YesYes, can count as a separate URLSorting, pagination, campaign tags
History API path (/pricing)YesYesSPA views, separate content pages

The phrase "Generally no" matches the wording in Google's documentation. Do not read it as an absolute rule.

In short, the decision tree is simple. If the content is on the same page, use a fragment. If the content is different, give it a real path. For optional variables such as sorting or campaigns, use a query string, but plan how those addresses behave with canonicals. Our post on the canonical tag explains that part.

How do you know your site uses hash based routing?

The symptoms usually show up in the first months. A few clear signs exist, and each is easy to check.

Watch for these signs:

  • When you browse the menu, the address bar shows a # followed by a path.
  • Google search shows only your home page or very few pages.
  • Search Console reports far fewer discovered pages than your site has views.
  • Analytics logs almost all sessions under one page address.
  • The page source lacks the text of the other views, because content arrives later.

If you see two or three of these signs together, inspect the setup. However, neither sign alone is proof. For example, a brand new site may simply not be crawled yet.

If you are new to Search Console, our guide on how to use Google Search Console covers the basic reports. For pages that stay out of the index, see how to find unindexed pages.

How do you audit a hash based site step by step?

You do not need advanced tools. So follow this order and you can clarify the situation within an hour.

  1. Open your site in a browser and click every menu link. Note how the address bar changes.
  2. Copy each view's address and open it directly in a new tab. Do you see the same content?
  3. Use the URL Inspection tool in Search Console to see how Google views those addresses.
  4. Then check the page source for the main text of each view.
  5. Then confirm the menu links are real link elements that carry an href value.
  6. Open the page level report in analytics and see whether the views show up on separate rows.

At the end you have two lists: views that have a real URL, and views that open only through a hash. The second list is the backbone of your fix plan.

Note: Menu and button names in tools change with interface updates. That is why we say "the relevant report" instead of an exact button label. You will find the match in your own panel.

How do you move from hash addresses to real URLs?

The hardest part of the migration is that the hash never reaches the server. Because of that, you cannot tell the server to send "#/pricing" to "/pricing". So you need a two layer plan.

The first layer is building the new structure. Give every view a real path, make sure the server answers those paths directly, and switch the old menu links to the new paths.

The second layer is handling visitors who arrive on old addresses. A small client side mapping, which runs after the page loads, can send them to the new path. However, this is not a true 301. It protects the visitor, but it does not guarantee signal transfer.

Keep these points in mind during the move:

  • Add all new paths to the sitemap.
  • Move internal links from the old hash format to the new paths.
  • Check pages that earn external links to old addresses first.
  • Watch the coverage report in Search Console for a few weeks after the change.

If you are unsure about redirect types, read our post on 301, 302, 307, and 308 redirects.

Why do status codes and soft 404s matter in SPAs?

In single page apps, routing happens on the client. So the server often returns a 200 status for every path. Google's documentation admits this: with client side routing, meaningful HTTP status codes can be impossible or impractical.

However, here is the catch. A visitor opens a path that does not exist. The app shows a "not found" message, but the server still returns 200. Google may treat this as a soft 404, a page that looks like an error but returns a success code.

First, the documentation offers two fixes. You can redirect to a real 404 page, or you can add a noindex robots meta tag to the error page with JavaScript. However, we do not write the tag as code here. Instead, your developer can open the documentation directly.

Also, this topic looks separate from the hash question, but it grows from the same architecture choice. Once you leave the hash and adopt the History API, you also own the job of answering invalid paths correctly. Our post on noindex versus nofollow explains noindex behavior.

How do you write links that Google can follow?

According to Google's links documentation, link discovery needs real link elements with an href attribute. Links added by JavaScript can also work, but they must follow the same rules.

For example, if a menu item is built as a button or a clickable box, Google may not see it as a link. Structures that switch to a hash without carrying an href fall into this group. The documentation also warns that addresses that use the "javascript" scheme are problematic because they do not resolve to a real web address.

Use this checklist:

  • Every menu item has an href that points to a real address.
  • Then a click handler adds behavior to the link and does not replace it.
  • Pagination and filter links also use real addresses.
  • Internal links have meaningful anchor text.

So this rule ties directly to the hash topic. Most hash based menus run on click events and expose no address that would make separate pages discoverable. For the speed impact of heavy scripts, see how JavaScript affects site speed.

What are the myths about the URL fragment?

The topic sounds technical, so a few wrong beliefs circle around it. We have heard all of them in the field.

  • "Google runs JavaScript, so the hash works." Running JavaScript is a separate matter. The issue is that a fragment is not treated as a separate page address.
  • "If I add the hash address to the sitemap, it gets indexed." A sitemap entry does not make Google treat the address as a separate page.
  • "A canonical tag separates hash views." A canonical manages duplicates. So it does not create a separate page.
  • "Hash is bad for all SEO." For on-page jumps, a hash is completely safe.
  • "Once I migrate, the problem is over." After the move you still check status codes, links, and the sitemap.

All of these myths share one trait: they look for the fix in tags or files. Instead, the real fix lives in the address itself. A view needs a unique, permanent address that the server can answer before it can rank.

To scan your site for other technical gaps, use our SEO checker. It will not fix a hash architecture alone, but it shows the basic gaps quickly.

Should you switch language or region with a URL fragment?

For example, some sites manage language with hash values such as example.com/#en and example.com/#de. In that setup, both languages sit at one address, and Google may see only one of them. A second page for the other language never forms.

A separate path per language is healthier, such as /en/ and /de/. Then each language gets its own page, title, and description. Linking language versions together is its own specialty.

Redirecting users automatically by IP address adds another risk. We examined that in our post on IP based language redirects. Here we only stress the point: language or country choice needs a real address, not a hash.

If the language switch is just an interface preference, for example when each language's text already sits in the HTML, a hash does no harm. The difference lies in which address holds the content.

How do you handle sunsetting hash addresses safely?

A migration goes wrong mostly through haste. Also, every mistake below is avoidable.

  • Half a migration: New paths work, but the menu still uses old hash links. As a result, Google cannot discover the new paths.
  • Returning 200 for everything: Missing paths also return success, which creates soft 404s.
  • Duplicate titles: Every view carries the same title. A view with its own path needs its own title.
  • Forgetting the sitemap: New paths are missing from the map, and discovery slows down.
  • Skipping analytics: Page view events do not fire on route changes, so sessions still look like one page.
  • Going live untested: Publishing without opening every path by hand first.

When you plan addresses, the principles in our post on URL slug rules help. For that reason, we do not repeat slugs here. We only remind you that a path should stay readable and permanent.

After the move, use the checklist in how to detect canonical issues in Search Console to watch for canonical errors.

When is a hash completely fine?

However, sometimes a hash is exactly the right tool. If your use falls into this group, you do not need to worry.

  • Jumping from a table of contents to a section.
  • A "back to top" link on a long page.
  • Showing which tab is open on the same page.
  • A link to a comment or footnote on the page.
  • Focusing a form's error message.

The common point is that the content already sits in the page HTML, and the hash only shows a position or a state. Google seeing the same address as one page is exactly what you want in that case.

So we are not against the hash. We are against hiding content that should be its own page behind a hash. Decide whether a piece of content gets its own searchable page. If it does, then give it a real path. If not, a hash is enough.

How do we handle this as a team?

As Talha Aslan and team, we start technical problems like this with discovery during SEO consulting work. On a hash based site, we first look at which views carry search value.

The process usually runs in this order:

  1. We list the current structure and the views.
  2. We use query data to decide which views need to be separate pages.
  3. Then we give your developers written requirements for real paths, status codes, and links.
  4. Finally, we monitor Search Console and analytics data after the move.

However, we do not promise results, because Google makes the indexing decision. What we do is remove the architectural barriers that stop Google from finding your content.

If you are planning a new site, making this decision at the start costs far less. In our web design service, we settle the routing structure during design. For a technical review of an existing site, see our SEO consulting page.

When should you ask for expert help?

In some cases, technical help saves time compared with going alone.

Consider help in these situations:

  • Your whole site runs on hash routing and needs a rewrite.
  • Traffic dropped after a migration and you cannot find the reason.
  • Your team lacks experience with architecture choices such as server side rendering.
  • Many external links point to old addresses and you risk losing signals.

Also, you can prepare before asking. List your views, mark which ones you want Google to find, and note your current traffic in analytics. Because of that, those three pieces of information speed up every technical conversation.

Finally, remember that there is no single right answer for hashes. The right fix depends on whether the content is a searchable page. If it should be searched, give it a real URL. If not, use the hash freely. To check traffic after a change, follow the steps in diagnosing a traffic drop with Search Console.

Frequently Asked Questions

Does Google index the part of a URL after the # sign?
Generally no. Google Search Central documentation says Google Search generally does not support URL fragments. So content that changes after the # does not show up as a separate searchable page. A hash that only jumps to a section on the same page changes no content, so it causes no trouble.
How do I make each view of my single page app a separate page?
Give each view a real path and handle routing with the History API. The address bar then shows a path like example.com/pricing. The server should answer that path directly, and menu links should be real links with an href. Each view also needs its own title.
Does Google still support hashbang (#!) URLs?
No. Google deprecated the hashbang and AJAX crawling scheme a long time ago and recommends a cleaner URL structure that needs neither # nor #!. If an older site still uses #! addresses, define a real path for each one and prepare a migration plan before you change anything.
Do # jump links hurt SEO?
No, they do not. The hash does not change the content here. It only scrolls to a heading on the same page. The content already sits in the HTML, and the target ID exists on the page. This pattern also helps readers and assistive technology users, so there is no reason to remove it.
What happens if I put a UTM tag after the #?
Your analytics data may break. The hash does not go to the server, and many setups read only the query string. Google Analytics Help tells you to separate parameters with a question mark. The safe order is tags after the ? and the hash last. Always test in your own setup.
Can I use a 301 redirect for old hash addresses?
No, because the server never sees the hash. Instead, build the new paths, switch the old menu links to them, and use a client side mapping for visitors who open an old address. That mapping does not guarantee signal transfer, so keep internal links and the sitemap current.
  • url fragment
  • hash indexing
  • single page application
  • history api
  • hashbang
  • javascript seo
  • utm parameters
  • technical seo
Share:
Talha Aslan

Google Partner digital marketing expert. Hands-on with SEO, Google Ads, web design and e-commerce projects since 2012; every post here comes from that experience.

Next project

Let's talk about your project.

Your brief goes straight to Talha Aslan and team: strategy led by Talha, delivery by an experienced team. The first consultation is free; we listen and come back with a clear roadmap.