Enterprise SPA Audits Made Simple
By Seth Black · Published · Updated
Big Angular and React apps are the sites that make most crawlers cry. Not because the tech is exotic anymore, but because the tools that claim to support SPAs usually mean “we fetch the HTML and hope for the best.” That works great until you audit a 200k-URL app where the title, canonical, and half the body copy don’t exist until the router finishes its job.
The playbook I’ve landed on is pretty short. Here’s what actually moves the needle.
Indexability on client-side routes
The first thing I check is whether the router is handing Google real URLs or decorative ones. If your /products/12345 route returns the same shell HTML as /about, and the product data only loads after a fetch, you need a crawler that waits for the DOM to settle. Otherwise every product page looks identical to the tool, and you’ll get a report full of duplicate titles that aren’t actually duplicates.
With --spa, BSA runs every page through headless Chrome, so what gets graded is the page after it renders, not the shell. That’s the bar. Anything less and you’re auditing a ghost.
Client-side routing traps
The sneaky failures are the ones that only happen for a subset of users. On one enterprise React site I ran last month, BSA flagged a redirect chain that only fired when the user-agent was mobile and the auth cookie was missing. Three hops, ending in a soft 404. The in-house team had never seen it because they were always logged in on desktop.
The stuff that actually bites you on SPAs:
- Links rendered as
<div onClick>instead of<a href>. Googlebot won’t follow them. - Route changes that update the URL but not the title or canonical.
- Lazy-loaded sections that never fire because the viewport logic assumes a real browser scroll.
- 404 routes that return a 200 status with a “not found” component.
These are exactly the class of problems covered in depth when you dig into JavaScript SEO rendering issues like hydration failures and SPA-driven LCP delays — most crawlers miss them entirely because they never actually render the page.
Bundle bloat and what it costs you
Rendering time is a ranking input, and it’s the thing your own team ignores because the app feels fine on their M2 MacBooks. A 4MB JS bundle on a mid-tier Android is a completely different experience. I pull the crawl data into pandas, join it with CrUX or RUM data from the client, and rank templates by real-user LCP. Now you have a list of things that are actually slow, not a Lighthouse score that shifts every time the wind changes.
BSA exports are scriptable, so merging crawl data with GSC and CrUX takes maybe an hour in a notebook, not a week of wrangling.
Running it at scale
Enterprise crawls are where most tools either force a sales call or quietly cap you at some URL count they won’t tell you about until you hit it. BSA has a free 14-day trial with every feature, no page limit, and no credit card, which means you can point it at a staging environment before anyone signs anything and find out whether it actually handles your stack. It runs locally and streams results to SQLite as it goes, so a 500k-URL crawl just needs a machine you can leave running.
If you’ve been hitting those walls with other tools, it’s worth seeing how BSA compares as a JavaScript-rendering crawler built for enterprise-scale audits without page caps or sales calls.
The loop I keep coming back to: crawl, find the render-specific problems, hand a concrete list to the programmers who own those components, fix, crawl again. When redirect chains and soft 404s surface from the crawl, the priority framework for fixing crawl errors without losing organic traffic is what I use to decide what goes into the sprint first. SPAs just need a crawler that actually renders the page before it calls the audit done.
BSA renders JavaScript in headless Chrome, so React and Angular sites audit against what actually loaded. See how or start a free trial.
-Sethers