# Why a Scan of a React or Next.js App Only Saw the Homepage

Canonical: https://vulnify.app/blog/why-a-javascript-app-scan-looks-thin

A thin report on a React or Next.js app usually means routes were never discovered. Crawl depth, browser rendering, login, and the API are four different gaps.

A vulnerability scan of a React or Next.js app that only reports the homepage is easy to misread as a clean application. It often means the scanner never saw the other routes. Route discovery, JavaScript execution, authentication, and API coverage are four different limits. Adding crawl depth fixes only one of them, and only when the extra pages are actually found. This article is about how to interpret that thin report. It is not a second copy of how to scan a website for vulnerabilities . If the real surface is an API described in a spec, use how to scan an API from an OpenAPI spec after you understand why the website crawl stopped early. What “only the homepage” usually means A website scan starts at the URL you typed. It can follow links it discovers, up to a page cap. If the homepage HTML contains no links to /account , /checkout , or /app/orders , those paths are not in the scan unless something else adds them. A single-page app often ships one HTML document and then builds the screen in the browser. The interesting URLs may exist only after that code runs, or only as clicks that never put an address in an anchor tag. The letter grade on that run describes the URLs that were tested. It does not describe the product. Say that in the ticket so nobody closes the work because the homepage looked fine. Route discovery is not the same as rendering Vulnify’s crawl uses a headless browser and reads links after the document has reached DOMContentLoaded . Links that are real &lt;a href&gt; elements by that moment can be collected. Routes that appear only after a later network call, or that live in a client router with no anchor, are still invisible. The browser does not click through the product. It does not wait for every spinner to finish. If the headless browser cannot start, discovery falls back to reading the raw HTML. That fallback really does miss client-rendered links, because the HTML shell often has none. A thin report after a fallback is a rendering miss. A thin report after a successful browser crawl is more often a navigation miss: the routes are not anchors, or they show up too late. Crawl depth does not rescan every new URL in full The crawl depth boost adds a browser crawl of up to 25 pages on top of any crawl already in the scan policy. With no policy crawl, that is 25 pages. The pages it discovers are checked for exposed files, redirects, directory listings, and odd HTTP methods. They do not each receive the full injection pass that the starting URL received. Buying a deeper crawl and expecting a full SQL injection test of every dashboard route will disappoint you, even when the links are found. Use the boost when you believe more ordinary pages are linked and you want those surface checks. Do not use it as a substitute for telling the scanner where the application lives. Aggressive active probes are a different add-on. They fuzz query parameters on crawled pages, and only on a verified hostname. They are not a general fix for a client-side router. Login walls hide the product on purpose A marketing homepage and a logged-in application are different sites from a scanner’s point of view. If every useful route redirects to a login form, the crawl records the login form. It does not invent a session. Authenticated scanning is a saved login profile, described in how to scan website pages that require a login . That profile is what lets later requests carry the cookie or token. Crawl depth without a login still cannot open the account area. Two profiles are for a different question: whether one account can open another account’s records. That starts with how to scan pages that require a login . Do not turn on a second user because the homepage looked empty. Turn it on when you already have object URLs and you want an ownership check. The API is often the real application Many Next.js and React products render a shell and then call /api or a separate host. Those endpoints may never appear as links on the homepage. An OpenAPI, Swagger, or Postman file is how you hand the scanner the list directly. The spec scan is not a deeper crawl of the same HTML. It is a different input. If the browser bundle contains a public API base URL, that is configuration, not a secret. What must not be in the bundle is a privileged key. Frontend API keys and NEXT_PUBLIC or VITE variables covers that mistake. Finding the API host in JavaScript does not mean the scan tested the API. It means you now know which spec or host to add. How to read the next report Before you call a JavaScript app clean, write down four lines. Which URLs were actually requested. Whether the crawl used the browser or the HTML fallback. Whether a login profile was attached. Whether an API spec was imported. If any line is “no,” the empty finding list applies only to the URLs that were fetched. Then decide the next run on purpose. Add the crawl boost when linked pages were cut off by the cap. Attach a login when the product is behind authentication. Import a spec when the behavior lives in JSON endpoints. Run the homepage scan anyway, because headers, TLS, cookies, and third-party scripts on the shell still matter. They are just not the whole application. A thin report is useful when you treat it as a map of what was seen. It is a false comfort when you treat it as a map of what you built. A second run you can compare Keep the first report. Run the next one with exactly one change: the login, the spec, or the crawl boost, not all three. If the URL list grows, the change did what you hoped. If the URL list stays a homepage, that change was the wrong lever and you still have the other two to try. Comparing two reports that differ in three settings teaches you nothing about which setting mattered. Write the URL list into the ticket, not only the grade. A grade of A on one URL is a different sentence from a grade of A on forty URLs. The person who approves the release can see the difference only if you show the list. When a route is still missing after a logged-in crawl, add it by importing the spec or by scanning that known path on purpose. Guessing that the scanner should have clicked will not create the request. Frequently asked questions Does a higher scan depth render the JavaScript app? Depth changes how much testing runs against the URLs in scope. It does not, by itself, click through a client-side router. Discovery is the crawl, and the crawl only collects links it can already see. Will crawl depth boost find my dashboard? Only if the dashboard is linked from pages the browser loads, within the page cap, and the link is an anchor rather than a click handler that never changes the href set. Logged-out homepages often have no such link. Use a login profile for those routes. Should I scan the API host as a website? You can, and you should still check TLS and headers there. Endpoint-level checks are more reliable when you import the API spec instead of hoping the crawl stumbles onto each path.
