Lazyload command
The lazyload command is used to download JavaScript files from a given URL or a list of URLs. It simulates various techniques to discover and fetch JS files that are loaded dynamically.
Usage
js-recon lazyload -u <url/file> [options]
Options
| Option | Alias | Description | Default | Required |
|---|---|---|---|---|
--url <url/list/file> | -u | Target URL, comma-separated URLs, or target file; may be repeated. See Run's -u examples. | Yes | |
--output <directory> | -o | Output directory to save the downloaded JS files. | output | No |
--strict-scope | Download JS files from only the input URL domain. | false | No | |
--scope <scope> | -s | Download JS files from specific domains (comma-separated). Use * for all domains. | * | No |
--threads <threads> | -t | Number of threads to use for downloading. | 1 | No |
--subsequent-requests | Download JS files from subsequent requests (Next.js only). | false | No | |
--urls-file <file> | Input JSON file containing URLs (for --subsequent-requests) | extracted_urls.json | No | |
--proxy-config <file> | Proxy config file, generated via js-recon proxy <method> -i. See Proxy. | .proxy_config.json | No | |
--ignore-proxy-env | Skip JS_RECON_* proxy environment variables during resolution. See Proxy. | false | No | |
--oxylabs-waf-fallback | Retry strongly identified CDN/WAF blocks through configured Oxylabs; direct requests remain the default. Opt-in — see Configuration. | false | No | |
--oxylabs-fallback-max-requests <n> | Maximum paid Oxylabs fallback requests per origin. | 10 | No | |
--oxylabs-fallback-max-total <n> | Maximum paid Oxylabs fallback requests per run. | 100 | No | |
--oxylabs-fallback-max-origins <n> | Maximum distinct fallback origins per run. | 25 | No | |
--cache-file <file> | File to contain response cache. | .resp_cache.json | No | |
--disable-cache | Disable response caching. | false | No | |
--cache-only | Only use the response cache; never make network requests. See Load command. | false | No | |
--yes | -y | Auto-approve executing JS code from the target. | false | No |
--timeout | Request timeout in ms | 30000 | No | |
--insecure | -k | Disable SSL certificate verification. | false | No |
--no-sandbox | Disable browser sandbox. | false | No | |
--build-id | Get the buildId from the Next.js app. | false | No | |
--sourcemap-dir <directory> | Directory to write reconstructed source maps. | extracted | No | |
--research | Enable research mode. | false | No | |
--research-output <file> | Output file for research mode. | research.json | No | |
--max-iterations <iterations> | Maximum number of recursive crawl iterations. | 10 | No | |
--max-js-size <mb> | Maximum JS file size in MB to parse (Vue only). | 2 | No | |
--lazyload-timeout <minutes> | Hard timeout for the lazyload module. The module stops and the pipeline continues after this many minutes. Use 0 to disable. | 30 | No | |
--detection-timeout <seconds> | Timeout for front-end framework detection. If it fires, detection falls back to the same "framework not detected" path used for a genuine no-match. Use 0 to disable. | 30 | No | |
--max-pages <pages> | Maximum number of HTML pages the Next.js crawler (or the generic tech recursive page crawl) will visit across all recursive passes. 0 disables the limit. Prevents memory exhaustion on event-heavy sites with large link graphs. | 200 | No | |
--max-redirects <n> | Maximum redirects to follow when resolving the default crawl scope for generic tech (see Generic extraction). | 20 | No | |
--strings | Enable strings-based recursive JS discovery for generic tech — chains the strings module into the crawl to find JS referenced only as a string literal inside an already-downloaded file. See Generic extraction. | false | No | |
--strings-max-iterations <n> | Maximum recursive strings-discovery passes for generic tech. 0 runs until a pass finds nothing new, with no cap. | 5 | No | |
--stagnation-timein <mins> | Minutes to wait before generic-tech content stagnation detection begins monitoring. 0 disables the feature. Must not exceed --lazyload-timeout (exit code 30 otherwise). See Generic extraction. | 30 | No | |
--stagnation-percentage <percent> | Percentage of all discovered generic-tech JS files (by content hash) that must share one hash to be flagged as stagnation. | 80 | No | |
--stagnation-monitor <mins> | Re-check interval for generic-tech stagnation detection once armed; also the debounce window used to confirm stagnation before stopping. | 1 | No | |
--include-methods <methods> | Comma-separated list of method names to run (whitelist). Only these methods will execute; all others are skipped. Use --list-methods to see valid names. See Lazyload Methods. | No | ||
--exclude-methods <methods> | Comma-separated list of method names to skip (blacklist). All methods except these will run. Use --list-methods to see valid names. See Lazyload Methods. | No | ||
--list-methods [framework] | Print all available method names grouped by framework and exit. Optionally provide a framework name (next_js, vue, nuxt_js, svelte, angular, react) to filter the output. | No | ||
--verbose | Show detailed file write error messages (e.g. when a downloaded JS chunk fails to write to disk). Suppressed by default to reduce terminal noise. | false | No |
How it works
Framework detection
Before downloading any files, the tool auto-detects which JavaScript framework the target uses. Detection runs in this priority order and stops on the first match:
- Next.js — any HTML element with a
src,srcset, orimageSrcSetattribute containing/_next/ - Vue.js — any element with a
data-v-*ordata-vue-*attribute; or__vuefound inside fetched script content - Nuxt.js — sub-check after Vue detection: any
src/hrefattribute containing/_nuxt - Svelte — SvelteKit-specific attribute markers or
__svelte_*in bundled code - Angular —
ng-*attributes or Angular-specific markers in bundled code - React — markers such as
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,__REACT_DEVTOOLS_GLOBAL_HOOK__,react-jsx-runtime.production, orreact-dom.productionin inline scripts or fetched assets
Detection uses two sources: the raw HTTP response (fast) and a Puppeteer-rendered page (catches client-side-only markers, after a 2-second settle delay). In --cache-only mode, the browser step is skipped.
Next.js discovery pipeline
Next.js receives the most comprehensive discovery. The crawler runs in two phases.
Initial phase (run once):
- Parse
<script src>tags and inlinestatic/chunks/...references on the landing page - Extract
<a href>links on the landing page for page-URL seeding - Execute the webpack runtime's chunk-loading function in a sandbox to enumerate all chunk IDs (requires
--yesto auto-confirm, or manual confirmation per run) - Parse
_buildManifest.jsAST forstatic/chunks/string references - Optionally, if
--subsequent-requestsis set: make RSC (RSC: 1header) and plain HTML requests to all discovered paths to find dynamically loaded chunks
Recursive phase (repeated until convergence or --max-iterations):
- Detect
Promise.all([...].map(...))patterns in newly downloaded chunks to extract additional chunk IDs - Parse
layout-*.jsfiles forhrefobject properties; visit discovered routes and extract their script tags - Re-run
<script src>and<a href>extraction on each newly discovered page URL - Stop when a full pass yields zero new URLs (convergence), the iteration cap is reached, or the page visit cap (
--max-pages) is reached
Page visit cap: The crawler counts every HTML page it visits across all recursive passes and stops adding more pages to the queue once the cap is hit. This prevents memory exhaustion on event-heavy or listing sites where every page links to dozens more — without a cap, the queue can fan out to hundreds of pages and exhaust the container's available RAM. The default cap is 200 pages, which is sufficient for virtually all real Next.js apps. Set
--max-pages 0to disable the cap entirely.
After all passes, .map is appended to every discovered .js URL and checked for a 200 response to find source maps.
Content-entropy deduplication: When the crawler encounters a page URL whose pathname has already been visited, it fetches the new URL and compares its
<script src>tags against every script set already recorded for that pathname. If the scripts are identical, the variant is skipped — it loads the same JS and would contribute nothing new. If the scripts differ (for example, a dynamically routed page that loads a unique chunk), the variant is visited and its script fingerprint is added. This lets the crawler correctly skip variants that differ only in a filter or selector parameter (for example,/search?sort=ascvs/search?sort=desc) while still visiting genuinely distinct parameterized routes (for example, different product or user pages that load unique chunks). The same fingerprint logic is applied in the script-tag subsequent-requests pass.
SvelteKit discovery pipeline
SvelteKit's chunk discovery depends on the build adapter.
adapter-node (SSR server): The HTML response does not include <link rel="modulepreload"> tags. Instead, the SvelteKit boot script is an inline <script> block with no src attribute:
<script>
Promise.all([
import("./_app/immutable/entry/start.js"),
import("./_app/immutable/entry/app.js")
]).then(...)
</script>
svelte_getFromPageSource extracts these two entry URLs via an import("...") regex. The full chunk graph is then discovered by following ESM import statements and string-scanning downloaded chunks.
adapter-static SSG and SPA: The shell HTML (404.html for SSG, index.html for SPA) contains both <link rel="modulepreload"> tags for all initial chunks and the same inline boot script. The modulepreload links provide a larger seed set (typically 17+ JS URLs) compared to the two entry points in the adapter-node case.
version.json probe: After page-source extraction, the tool probes /<appDir>/version.json (typically /_app/version.json). SvelteKit generates this file at build time for the updated store — it is never linked from any HTML tag or JS import() call, so all other discovery steps miss it. The appDir is inferred from the entry-point URLs already found (default: _app). This step can be skipped with --exclude-methods svelte_getVersionJson.
Detection signal: All three adapters are detected via the _app/immutable/ path prefix on JS or CSS links in the HTML response.
--yes flag and JS execution
The webpack chunk-enumeration technique extracts a function from the webpack runtime and executes it locally in a Node.js sandbox with each discovered integer chunk ID as input. Before executing, the tool prompts you to inspect the extracted function and confirm. Pass --yes to skip the prompt — useful in automated pipelines, but verify you trust the target's JS first.
Scope
| Flag | Behaviour |
|---|---|
(default) * | Download JS from any domain |
--scope a.com,b.com | Only download from a.com and b.com |
--strict-scope | Only download from the exact host in the input URL |
Scoping matters most when JS assets are served from a CDN subdomain. The run command auto-detects CDN hosts and adjusts the map directory accordingly, but lazyload alone requires explicit scope configuration.
Framework Support
Each framework is added to the tool after thorough research on the framework. New techniques are added when they are discovered. The following is an exhaustive list of frameworks that the lazyload module is compatible with:
- Next.js
- Vue
- Nuxt
- Angular
- React
- Svelte
Please note that some frameworks are supported better than others. Currently, the frameworks with the most supported techniques are Next.js and Vue.
Examples
List available methods
Print all available discovery method names:
js-recon lazyload --list-methods
Filter by framework:
js-recon lazyload --list-methods next_js
Run only specific methods (whitelist)
Run only the script-tag extraction method for a Next.js target:
js-recon lazyload -u https://example.com -y --include-methods next_GetJSScript
Skip specific methods (blacklist)
Skip the brute-force fallback and webpack analysis for a faster run:
js-recon lazyload -u https://example.com -y --exclude-methods next_bruteForceJsFiles,next_GetLazyResourcesWebpackJs
Basic usage
Download all JavaScript files from a single URL:
js-recon lazyload -u https://example.com
Setting scope
Download JavaScript files only from example.com and cdn.example.com:
js-recon lazyload -u https://example.com -s "example.com,cdn.example.com"
Using the --strict-scope will only download JS files from the URL provided. This will skip any files from the external CDN.
Using a proxy
Route requests through a proxy — AWS API Gateway (IP rotation), a generic SOCKS5/HTTP proxy, or
Oxylabs. First configure the method with the matching proxy command, for example
js-recon proxy aws -i, then point lazyload at the resulting config file:
js-recon proxy aws -i
js-recon lazyload -u https://example.com --proxy-config .proxy_config.json
Read the docs of Proxy for more information.