August 9, 2026
How I Prioritize Hundreds of Subdomains During Bug Bounty Recon
Finding 500, 1,000, or even 10,000 subdomains isn’t the difficult part of bug bounty reconnaissance.
By Rakesh Joshi
6 min read
The difficult part is knowing which ones deserve your attention first.
A list like:
www.example.com
api.example.com
dev.example.com
staging.example.com
admin.example.com
cdn.example.com
old.example.com
test.example.com
vpn.example.com
...www.example.com
api.example.com
dev.example.com
staging.example.com
admin.example.com
cdn.example.com
old.example.com
test.example.com
vpn.example.com
...isn't a vulnerability.
The real advantage comes from turning that huge asset list into a small, prioritized attack surface.
My approach is:
Enumeration
↓
Deduplication
↓
DNS validation
↓
HTTP probing
↓
Technology identification
↓
Environment classification
↓
Interesting endpoint discovery
↓
Risk scoring
↓
Manual testingEnumeration
↓
Deduplication
↓
DNS validation
↓
HTTP probing
↓
Technology identification
↓
Environment classification
↓
Interesting endpoint discovery
↓
Risk scoring
↓
Manual testing1. Start With Scope
Before running any reconnaissance command, define the exact authorized scope.
For example:
*.example.com*.example.comdoesn't automatically mean every third-party service discovered through DNS is authorized for testing.
Create a working directory:
mkdir -p recon/example
cd recon/examplemkdir -p recon/example
cd recon/exampleStore your scope separately:
nano scope.txtnano scope.txtExample:
example.com
*.example.comexample.com
*.example.comYour reconnaissance workflow should always respect the program's published scope and exclusions.
2. Collect Subdomains
There isn't one perfect enumeration source.
I prefer combining multiple passive sources.
For example, with ProjectDiscovery's subfinder:
subfinder -d example.com -all -silent -o subdomains.txtsubfinder -d example.com -all -silent -o subdomains.txtYou can also use certificate transparency data:
curl -s "https://crt.sh/?q=%25.example.com&output=json" \
| jq -r '.[].name_value' \
| sed 's/\*\.//g' \
| sort -u > crt.txtcurl -s "https://crt.sh/?q=%25.example.com&output=json" \
| jq -r '.[].name_value' \
| sed 's/\*\.//g' \
| sort -u > crt.txtMerge the results:
cat subdomains.txt crt.txt | sort -u > all-subdomains.txtcat subdomains.txt crt.txt | sort -u > all-subdomains.txtCheck the size:
wc -l all-subdomains.txtwc -l all-subdomains.txtSuppose the result is:
1847 all-subdomains.txt1847 all-subdomains.txtNow the real work begins.
3. Don't Treat Every Subdomain Equally
Consider these assets:
www.example.com
cdn.example.com
static.example.com
api.example.com
admin.example.com
dev.example.com
staging.example.com
jenkins.example.com
grafana.example.com
old.example.comwww.example.com
cdn.example.com
static.example.com
api.example.com
admin.example.com
dev.example.com
staging.example.com
jenkins.example.com
grafana.example.com
old.example.comThey shouldn't receive equal attention.
A useful first classification is:
CategoryPriorityAdmin interfacesVery HighDevelopment environmentsVery HighStaging environmentsVery HighInternal-looking servicesVery HighAPIsHighAuthentication servicesHighFile/storage servicesHighBusiness applicationsHighLegacy applicationsHighMarketing websitesMediumCDN/static assetsLowImage/static hostsLow
This immediately reduces the search space.
4. Find Live Hosts
A subdomain existing in DNS doesn't mean an application is actually running.
Use HTTP probing:
httpx -l all-subdomains.txt \
-silent \
-o live.txthttpx -l all-subdomains.txt \
-silent \
-o live.txtNow you might go from:
1847 discovered1847 discoveredto:
312 responding312 respondingThat's a much more manageable attack surface.
For richer information:
httpx -l all-subdomains.txt \
-status-code \
-title \
-tech-detect \
-follow-redirects \
-web-server \
-o httpx.txthttpx -l all-subdomains.txt \
-status-code \
-title \
-tech-detect \
-follow-redirects \
-web-server \
-o httpx.txtExample output:
https://api.example.com [200] [Example API] [nginx]
https://admin.example.com [200] [Admin Portal] [nginx]
https://staging.example.com [200] [Staging App] [Apache]
https://dev.example.com [403] [Development] [nginx]
https://cdn.example.com [200] [CDN]https://api.example.com [200] [Example API] [nginx]
https://admin.example.com [200] [Admin Portal] [nginx]
https://staging.example.com [200] [Staging App] [Apache]
https://dev.example.com [403] [Development] [nginx]
https://cdn.example.com [200] [CDN]The 403 result is still interesting.
403 does not mean "not interesting."
It means the server responded and access is restricted.
5. Extract Interesting Names
One of my simplest prioritization techniques is keyword analysis.
Search for development and administrative naming patterns:
grep -Ei \
'admin|administrator|dev|development|stage|staging|test|testing|uat|qa|internal|vpn|portal|dashboard|jenkins|grafana|git|api|backend|legacy|old' \
all-subdomains.txt \
> interesting-hostnames.txtgrep -Ei \
'admin|administrator|dev|development|stage|staging|test|testing|uat|qa|internal|vpn|portal|dashboard|jenkins|grafana|git|api|backend|legacy|old' \
all-subdomains.txt \
> interesting-hostnames.txtNow inspect:
cat interesting-hostnames.txtcat interesting-hostnames.txtYou might discover:
admin.example.com
dev.example.com
staging.example.com
api.example.com
grafana.example.com
jenkins.example.com
old-api.example.com
internal-portal.example.comadmin.example.com
dev.example.com
staging.example.com
api.example.com
grafana.example.com
jenkins.example.com
old-api.example.com
internal-portal.example.comThese deserve earlier investigation than:
img.example.com
static.example.com
fonts.example.com
cdn.example.comimg.example.com
static.example.com
fonts.example.com
cdn.example.com6. Prioritize by Environment
Hostname naming can reveal application environments.
For example:
app.example.com
dev.example.com
staging.example.com
uat.example.com
qa.example.com
prod.example.comapp.example.com
dev.example.com
staging.example.com
uat.example.com
qa.example.com
prod.example.comDevelopment environments can be particularly valuable because they may have:
- incomplete authentication
- debugging features
- verbose error messages
- test accounts
- development APIs
- older dependencies
- experimental functionality
- weaker security controls
However, the hostname alone is not evidence of a vulnerability.
Treat it as a prioritization signal.
7. Look at HTTP Status Codes
Extract hosts returning interesting status codes:
grep '\[200\]' httpx.txt > status-200.txt
grep '\[401\]' httpx.txt > status-401.txt
grep '\[403\]' httpx.txt > status-403.txtgrep '\[200\]' httpx.txt > status-200.txt
grep '\[401\]' httpx.txt > status-401.txt
grep '\[403\]' httpx.txt > status-403.txtWhy?
Because different responses tell you different things.
200
Potentially accessible application.
https://admin.example.com [200]https://admin.example.com [200]High priority if it appears to be an administrative interface.
401
Authentication exists.
https://api.example.com [401]https://api.example.com [401]This may be useful for understanding the application's authentication boundary.
403
Access control exists.
https://internal.example.com [403]https://internal.example.com [403]Worth understanding, but don't attempt unauthorized bypasses.
404
The hostname works, but the requested resource wasn't found.
Don't automatically discard it.
8. Technology Fingerprinting
Technology can dramatically change priority.
Run:
httpx -l all-subdomains.txt \
-tech-detect \
-title \
-status-code \
-o technologies.txthttpx -l all-subdomains.txt \
-tech-detect \
-title \
-status-code \
-o technologies.txtYou might see:
admin.example.com [200] [Admin Portal] [React, Node.js]
api.example.com [200] [API] [Express]
legacy.example.com [200] [Legacy System] [PHP]
jenkins.example.com [403] [Jenkins] [Jenkins]
grafana.example.com [200] [Grafana] [Grafana]admin.example.com [200] [Admin Portal] [React, Node.js]
api.example.com [200] [API] [Express]
legacy.example.com [200] [Legacy System] [PHP]
jenkins.example.com [403] [Jenkins] [Jenkins]
grafana.example.com [200] [Grafana] [Grafana]Now your attack surface has context.
Instead of:
312 live hosts312 live hostsyou have:
23 APIs
8 admin portals
6 development environments
4 staging environments
3 monitoring systems
...23 APIs
8 admin portals
6 development environments
4 staging environments
3 monitoring systems
...That's much more useful.
9. Identify APIs
API hosts are particularly interesting because modern applications often expose significant functionality through APIs.
Search:
grep -Ei 'api|graphql|gateway|backend|service' \
all-subdomains.txt \
> api-candidates.txtgrep -Ei 'api|graphql|gateway|backend|service' \
all-subdomains.txt \
> api-candidates.txtProbe them:
httpx -l api-candidates.txt \
-status-code \
-title \
-tech-detect \
-o api-results.txthttpx -l api-candidates.txt \
-status-code \
-title \
-tech-detect \
-o api-results.txtYou can also inspect common documentation paths within authorized targets:
curl -i https://api.example.com/curl -i https://api.example.com/If the application publicly exposes documentation, you might encounter references to:
/openapi.json
/swagger.json
/api-docs
/graphql/openapi.json
/swagger.json
/api-docs
/graphqlDon't assume that discovering an endpoint means you should attack it. First determine whether it is in scope and what testing is permitted.
10. Search JavaScript on High-Priority Applications
JavaScript can reveal additional attack surface.
For an authorized target:
curl -s https://app.example.com/ \
| grep -oE 'src="[^"]+\.js[^"]*"'curl -s https://app.example.com/ \
| grep -oE 'src="[^"]+\.js[^"]*"'For a larger workflow, tools such as katana can crawl URLs:
katana -u https://app.example.com \
-silent \
-o urls.txtkatana -u https://app.example.com \
-silent \
-o urls.txtThen identify JavaScript:
grep -Ei '\.js($|\?)' urls.txt > javascript.txtgrep -Ei '\.js($|\?)' urls.txt > javascript.txtPotential discoveries include:
/api/
/graphql
/oauth/
/admin/
/internal/
/v2/
/users/
/billing//api/
/graphql
/oauth/
/admin/
/internal/
/v2/
/users/
/billing/This is where subdomain prioritization becomes application mapping.
11. Don't Ignore Legacy Hosts
Some of the most interesting assets aren't the newest ones.
Consider:
app.example.com
api.example.com
legacy.example.com
old-api.example.com
v1.example.comapp.example.com
api.example.com
legacy.example.com
old-api.example.com
v1.example.comThe production application might be heavily hardened while an older service still exists.
Search for naming patterns:
grep -Ei \
'old|legacy|archive|backup|v1|v2|deprecated|previous|temp|test' \
all-subdomains.txt \
> legacy-candidates.txtgrep -Ei \
'old|legacy|archive|backup|v1|v2|deprecated|previous|temp|test' \
all-subdomains.txt \
> legacy-candidates.txtThen probe:
httpx -l legacy-candidates.txt \
-status-code \
-title \
-tech-detect \
-o legacy-results.txthttpx -l legacy-candidates.txt \
-status-code \
-title \
-tech-detect \
-o legacy-results.txtA legacy hostname isn't automatically vulnerable.
But it deserves attention because security controls and software versions may differ from the primary application.
12. Investigate DNS Relationships
DNS can reveal infrastructure relationships.
Basic lookup:
dig api.example.comdig api.example.comCNAME:
dig CNAME api.example.comdig CNAME api.example.comA records:
dig A api.example.comdig A api.example.comYou may discover something like:
api.example.com
↓
api.example.cloud-provider.exampleapi.example.com
↓
api.example.cloud-provider.exampleThis tells you the application may be backed by a cloud service or third-party infrastructure.
Again:
Interesting infrastructure ≠ authorized target.
Use DNS information primarily for understanding the architecture and identifying assets that are actually within scope.
13. Build a Priority Score
This is where I turn reconnaissance into a repeatable process.
Give each host a score.
Example:
SignalScoreAdmin interface+5Development environment+5Staging environment+5API+4Authentication service+4Legacy application+4Internal-looking hostname+4Sensitive business function+5200 response+2401/403 response+1Interesting technology+2Static/CDN host-2
For example:
admin.example.comadmin.example.comcould become:
Admin +5
200 +2
Application +2
----------------
Total 9Admin +5
200 +2
Application +2
----------------
Total 9While:
cdn.example.comcdn.example.commight receive:
CDN -2
Static -2
200 +2
----------------
Total -2CDN -2
Static -2
200 +2
----------------
Total -2You don't have to use exactly these numbers.
The important thing is consistency.
14. Automate Basic Prioritization
A simple shell workflow can create a candidate list:
grep -Ei \
'admin|dev|stage|staging|test|uat|qa|internal|portal|dashboard|api|backend|legacy|old|jenkins|grafana' \
all-subdomains.txt \
| sort -u \
> high-priority.txtgrep -Ei \
'admin|dev|stage|staging|test|uat|qa|internal|portal|dashboard|api|backend|legacy|old|jenkins|grafana' \
all-subdomains.txt \
| sort -u \
> high-priority.txtThen probe only those hosts:
httpx -l high-priority.txt \
-status-code \
-title \
-tech-detect \
-follow-redirects \
-o high-priority-results.txthttpx -l high-priority.txt \
-status-code \
-title \
-tech-detect \
-follow-redirects \
-o high-priority-results.txtThis creates a feedback loop:
1847 domains
↓
312 live
↓
67 interesting
↓
25 high priority
↓
10 manually investigated1847 domains
↓
312 live
↓
67 interesting
↓
25 high priority
↓
10 manually investigatedThat is much more efficient than manually opening 1,847 domains.
15. Think in Attack Paths, Not Just Hosts
This is the biggest mindset change.
Don't ask:
"Which subdomain looks vulnerable?"
Ask:
"Which subdomain gives me the most interesting path through the application's architecture?"
For example:
dev.example.com
↓
JavaScript
↓
api.example.com
↓
Authentication
↓
Business functionality
↓
Authorization boundarydev.example.com
↓
JavaScript
↓
api.example.com
↓
Authentication
↓
Business functionality
↓
Authorization boundaryAnother:
staging.example.com
↓
Different application version
↓
Different API
↓
Different authorization modelstaging.example.com
↓
Different application version
↓
Different API
↓
Different authorization modelOr:
legacy.example.com
↓
Old application
↓
Old API
↓
Shared authentication
↓
Potential security boundarylegacy.example.com
↓
Old application
↓
Old API
↓
Shared authentication
↓
Potential security boundaryThis approach is much more powerful than simply looking for vulnerable software versions.
16. My Practical Priority Order
When faced with hundreds of subdomains, my rough order is:
1. Admin / privileged interfaces
2. APIs
3. Development environments
4. Staging environments
5. Authentication / SSO
6. Business-critical applications
7. Legacy applications
8. Internal-looking services
9. File/storage applications
10. Monitoring / management interfaces
11. Normal production applications
12. Marketing websites
13. CDN / static assets1. Admin / privileged interfaces
2. APIs
3. Development environments
4. Staging environments
5. Authentication / SSO
6. Business-critical applications
7. Legacy applications
8. Internal-looking services
9. File/storage applications
10. Monitoring / management interfaces
11. Normal production applications
12. Marketing websites
13. CDN / static assetsThis isn't a universal ranking.
The application's architecture and the bug bounty program's scope should always determine the final priority.
17. The 30-Minute Workflow
If I receive a large scope, I don't immediately start testing everything.
Phase 1 — Enumeration
subfinder -d example.com -all -silent -o subdomains.txtsubfinder -d example.com -all -silent -o subdomains.txtPhase 2 — Deduplication
sort -u subdomains.txt -o subdomains.txtsort -u subdomains.txt -o subdomains.txtPhase 3 — HTTP probing
httpx -l subdomains.txt \
-status-code \
-title \
-tech-detect \
-silent \
-o httpx.txthttpx -l subdomains.txt \
-status-code \
-title \
-tech-detect \
-silent \
-o httpx.txtPhase 4 — Keyword prioritization
grep -Ei \
'admin|api|dev|stage|staging|test|uat|qa|internal|portal|dashboard|legacy|old' \
subdomains.txt \
> priority.txtgrep -Ei \
'admin|api|dev|stage|staging|test|uat|qa|internal|portal|dashboard|legacy|old' \
subdomains.txt \
> priority.txtPhase 5 — Probe priority targets
httpx -l priority.txt \
-status-code \
-title \
-tech-detect \
-follow-redirects \
-o priority-results.txthttpx -l priority.txt \
-status-code \
-title \
-tech-detect \
-follow-redirects \
-o priority-results.txtPhase 6 — Manually investigate
Now I focus on:
Application behavior
Authentication
Authorization
API functionality
Business logic
Input handling
Information exposure
Security boundariesApplication behavior
Authentication
Authorization
API functionality
Business logic
Input handling
Information exposure
Security boundaries18. What I DON'T Do
Good reconnaissance is also about avoiding wasted effort.
I don't automatically:
❌ Attack every discovered subdomain
❌ Scan every port aggressively
❌ Assume "admin" means vulnerable
❌ Treat a version number as a vulnerability
❌ Test third-party infrastructure outside scope
❌ Run destructive payloads
❌ Ignore program-specific rate limits
❌ Report scanner output without validation❌ Attack every discovered subdomain
❌ Scan every port aggressively
❌ Assume "admin" means vulnerable
❌ Treat a version number as a vulnerability
❌ Test third-party infrastructure outside scope
❌ Run destructive payloads
❌ Ignore program-specific rate limits
❌ Report scanner output without validationInstead:
Discover → Understand → Prioritize → Validate → ReportDiscover → Understand → Prioritize → Validate → Report19. The Real Goal
The goal of reconnaissance isn't:
"Find as many subdomains as possible."
The goal is:
"Understand the organization's attack surface well enough to know where security weaknesses are most likely to have meaningful impact."
A thousand random subdomains are less valuable than twenty well-understood applications.
The best bug bounty recon workflow therefore looks like:
DISCOVER
↓
1,000+ assets
↓
FILTER
↓
300 live
↓
CLASSIFY
↓
80 useful
↓
PRIORITIZE
↓
20 targets
↓
UNDERSTAND
↓
Application logic
↓
TEST
↓
Valid vulnerability
↓
VALIDATE
↓
REPORTDISCOVER
↓
1,000+ assets
↓
FILTER
↓
300 live
↓
CLASSIFY
↓
80 useful
↓
PRIORITIZE
↓
20 targets
↓
UNDERSTAND
↓
Application logic
↓
TEST
↓
Valid vulnerability
↓
VALIDATE
↓
REPORT**Recon isn't about collecting more data.
It's about reducing uncertainty.**