FProbe: Fast HTTP Probing with Inline Port Specification for Security Reconnaissance
Hook
Most HTTP probing tools force you to choose between scanning a fixed port list or writing bash loops to iterate through custom ports per host. FProbe eliminates this choice by supporting inline port specification—letting you probe example.com:8080,8443,9000 in a single entry.
Context
In security reconnaissance workflows, identifying live HTTP services is a critical step between subdomain enumeration and exploitation. You've run Amass or Subfinder and have thousands of subdomains. Now you need to know which ones actually respond to HTTP requests. Early tools like httprobe solved this for standard ports (80, 443, 8080, 8443), but real-world applications run on arbitrary ports discovered through port scanning.
The traditional approach requires awkward pipelines: scan with Masscan to find open ports, parse the output, generate per-host port lists, then feed them to a probing tool that may not even support custom ports per host. You end up writing bash loops that serialize what should be concurrent operations, or you probe every host on every possible port (wasteful and slow). FProbe was built to bridge the gap between port scanners and HTTP probers, accepting the messy reality of mixed port lists and turning them into clean, fast validation of web services.
Technical Insight
FProbe's architecture revolves around flexible input parsing and controlled concurrency. Written in Go, it leverages goroutines for parallel probing while maintaining configurable resource limits. The tool's standout feature is its input format flexibility—it accepts plain domains, domains with inline ports, CIDR ranges, and full URLs, all in the same input stream.
The inline port syntax is deceptively simple but architecturally significant. Instead of requiring separate files or complex pipeline logic, you can specify:
echo "example.com:8080,8443,9000" | fprobe
This expands internally into three separate probe operations for example.com on ports 8080, 8443, and 9000. The parser splits on commas and creates independent probe jobs, each handled by the worker pool. This design choice means FProbe can consume output from tools like Nmap or Masscan with minimal processing:
# Nmap XML output to FProbe input
nmap -p- --open -oX scan.xml targets.txt
xmlstarlet sel -t -m "//host" -v "concat(address/@addr,':',ports/port/@portid)" -n scan.xml | fprobe
The concurrency model uses a semaphore pattern implemented with buffered channels. By default, FProbe spawns 50 concurrent workers, but this is configurable via the -c flag. Each worker pulls from a job queue, performs the HTTP probe with timeout controls (default 9 seconds), and writes results to stdout. The implementation avoids common pitfalls like goroutine leaks by using sync.WaitGroup to ensure all workers complete before exit:
// Simplified conceptual example of FProbe's worker pattern
func probe(jobs <-chan string, results chan<- ProbeResult, wg *sync.WaitGroup, timeout time.Duration) {
defer wg.Done()
for target := range jobs {
client := &http.Client{
Timeout: timeout,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse // Don't follow redirects
},
}
resp, err := client.Get(target)
if err == nil {
results <- ProbeResult{
URL: target,
StatusCode: resp.StatusCode,
ContentType: resp.Header.Get("Content-Type"),
}
resp.Body.Close()
}
}
}
The CIDR expansion feature is another thoughtful design decision. When FProbe encounters CIDR notation like 192.168.1.0/24, it expands the range and creates probe jobs for each IP. This is particularly useful when you want to quickly validate web services across an IP block discovered during reconnaissance:
echo "192.168.1.0/24" | fprobe -p 80,443,8080,8443
This generates 1024 probe attempts (256 IPs × 4 ports), all processed concurrently within the worker pool limits. The expansion happens during input processing, not during probing, keeping the worker logic simple and focused.
FProbe's output modes cater to different pipeline needs. The default mode outputs only successful URLs, perfect for feeding into the next tool in your chain. The verbose JSON mode (-v flag) provides structured data including status codes, content types, server headers, and redirect locations—ideal for automated filtering:
fprobe -v < subdomains.txt | jq 'select(.status_code == 200 and (.content_type | contains("application/json")))'
This filters for APIs returning JSON, a common pattern when hunting for REST endpoints. The tool intelligently handles both HTTP and HTTPS, attempting HTTPS first when using the prefer-HTTPS mode (-s flag), then falling back to HTTP only if HTTPS fails. This reduces probe attempts while still catching HTTP-only services.
Gotcha
FProbe's simplicity is both its strength and its limitation. The tool provides no retry logic, meaning transient network failures result in missed services. If a target times out once, FProbe moves on—there's no exponential backoff or configurable retry count. For large scans across unreliable networks, you'll see false negatives that require re-running the tool or implementing retry logic in your wrapper scripts.
The timeout configuration is global, not per-host or adaptive. The 9-second default works for most cases, but slow-loading applications may get cut off, while fast networks waste time waiting. There's no request customization beyond basic HTTP GET requests—you can't inject custom headers, modify User-Agent strings (it uses Go's default), or perform POST requests. This makes it unsuitable for probing services that require specific headers like API keys or authentication tokens. Additionally, FProbe doesn't provide rate limiting controls. The concurrency flag limits simultaneous connections but doesn't throttle requests per second, which can trigger rate limits or WAF blocks on target networks. For red team operations requiring stealth, you'll need to add external rate limiting with tools like pv or write wrapper scripts that batch inputs.
Verdict
Use FProbe if you're building reconnaissance pipelines that integrate port scanning with HTTP service discovery, especially when dealing with non-standard ports or CIDR ranges. Its inline port syntax eliminates awkward bash loops and makes it trivial to consume Nmap/Masscan output. It's perfect for bug bounty recon where speed matters more than deep fingerprinting, and you just need to know which hosts respond to HTTP before passing them to specialized tools. The lightweight binary and minimal dependencies make it ideal for running on disposable VPS instances or inside Docker containers during automated scans. Skip FProbe if you need advanced features like technology fingerprinting, screenshot capture, custom headers, or retry logic. Modern alternatives like httpx offer significantly richer functionality with pipeline modes, WAF detection, and custom DSL filters. Also skip it if you're probing services that require authentication or specific headers—FProbe's rigid GET-only approach won't work. Finally, if you need detailed error reporting and logging for compliance or auditing purposes, FProbe's minimal output won't meet your requirements. For simple alive-or-dead HTTP checks with flexible port handling, FProbe excels; for everything else, reach for a more feature-complete alternative.