Features
- Concurrent scanning โ a bounded worker pool runs many connections at once with a configurable limit, so you stay fast without exhausting file descriptors.
-
TCP and UDP scanning โ switch transport
with
--protocol. UDP probes well-known services and reports unanswered ports asopen|filtered. -
Flexible port specs โ mix inclusive
ranges and lists:
1-1024,80,443,8080, or22,80,8000-8010. - Service fingerprinting โ banner grabbing plus a signature database identifies common services like SSH, nginx, Apache, MySQL, and Redis.
- Configurable timeouts โ tune connection timeouts to suit slow, filtered, or distant hosts.
-
Rate limiting โ cap how many new probes
start each second with
--rateto reduce network noise and load on the target. - Clean output, JSON & HTML โ a neatly aligned table for humans, structured JSON export for scripts and pipelines, plus a self-contained, styled HTML report you can open in any browser.
-
Featherweight โ built on Node's
built-in
netanddgrammodules, with no heavy dependencies to install or audit.
Installation
๐ณ Run with Docker (recommended)
The quickest way to run NodeJS-PortScanner โ no Node.js install or clone required. Pull the pre-built image from the GitHub Container Registry; any flags after the image name are passed straight to the scanner.
$ docker run --rm ghcr.io/zuedev/nodejs-portscanner --host example.com --ports 1-1024
$ docker run --rm ghcr.io/zuedev/nodejs-portscanner --help
On Linux, scanning the host's own
interfaces or localhost needs the host network
โ otherwise the scan runs from the container's isolated
namespace:
$ docker run --rm --network host ghcr.io/zuedev/nodejs-portscanner --host 127.0.0.1 --ports 1-1024
๐ช On Windows & macOS (Docker Desktop),
--network host shares the network of Docker's
internal Linux VM โ not your machine โ so
127.0.0.1 won't reach services running on your
host. Drop --network host and target
host.docker.internal instead, which Docker
Desktop routes to the host:
$ docker run --rm ghcr.io/zuedev/nodejs-portscanner --host host.docker.internal --ports 1-1024
If a service is bound only to 127.0.0.1 on the
host (rather than 0.0.0.0), rebind it to all
interfaces so the container can reach it.
Save a JSON or HTML report by mounting a writable directory
and writing into it โ a .html extension
produces an HTML report, any other writes JSON:
$ docker run --rm -v "${PWD}:/data" ghcr.io/zuedev/nodejs-portscanner -h example.com -p 1-1024 -o /data/results.json
# On Linux, add --user "$(id -u):$(id -g)" so the file is owned by you.
Prefer to build the image yourself? From a clone of the repo:
$ docker build -t nodejs-portscanner .
$ docker run --rm nodejs-portscanner --host example.com --ports 1-1024
From source
Requires Node.js ≥ 25. No build step.
$ git clone https://github.com/zuedev/NodeJS-PortScanner.git
$ cd NodeJS-PortScanner
$ npm install
Or install globally to expose a
nodejs-portscanner command on your
PATH:
$ npm install -g .
$ nodejs-portscanner --host 192.168.1.1 --ports 1-1024
Quick start
-
Scan a host you own:
$ node scanner.js --host 192.168.1.1 --ports 1-1024 - Read the table โ open ports are listed with their detected service.
-
Save a report:
$ node scanner.js --host 192.168.1.1 --ports 1-1024 --output results.json
Use cases
Practical, lawful ways to put NodeJS-PortScanner to work on infrastructure you control.
๐ฅ๏ธ Audit your own server
Confirm exactly which ports your VPS or home server exposes to the network, and spot anything you didn't intend to leave open.
$ node scanner.js -h my-server.lan -p 1-65535 -c 200
๐ Inventory your home network
Discover what services are running on devices across your LAN โ printers, NAS boxes, IoT gadgets โ and fingerprint them.
$ node scanner.js -h 192.168.1.10 -p 1-1024
๐งฑ Verify firewall rules
After changing firewall or security-group rules, prove that the ports you closed are actually filtered and the ones you need stay reachable.
$ node scanner.js -h 10.0.0.5 -p 22,80,443,3306 -t 1500
๐ Pre-deployment smoke check
Before going live, confirm a fresh box only answers on the
services you expect (for example, just 80 and
443).
$ node scanner.js -h staging.internal -p 1-1024 -o pre-deploy.json
๐ค CI/CD service checks
Import the API into a test to assert that a freshly started service is listening on its expected port before the pipeline continues.
$ node --test # uses scanPorts() under the hood
๐ Learn how scanning works
The code is small and readable โ a hands-on way to understand TCP connect scans, banner grabbing, and concurrency pools.
$ node scanner.js -h 127.0.0.1 -p 1-1024
Usage & examples
Common command-line recipes. Every flag has a short alias.
Basic scan
Scan the default range (1-1024).
$ node scanner.js --host 192.168.1.1 --ports 1-1024
Scan specific ports
Pass a comma-separated list.
$ node scanner.js --host example.com --ports 22,80,443,8080
Tune concurrency and timeout
Scan every port with 200 simultaneous connections and a 1.5s timeout.
$ node scanner.js --host 10.0.0.5 --ports 1-65535 --concurrency 200 --timeout 1500
Scan UDP ports
Switch to UDP with --protocol. Tailored probes
are sent to common services (DNS, NTP); ports that never
reply are reported as open|filtered.
$ node scanner.js --host 192.168.1.1 --protocol udp --ports 53,123,161
Limit the scan rate
Cap how many new probes start each second across all workers to keep the scan quiet and gentle on the target.
$ node scanner.js --host 192.168.1.1 --ports 1-1024 --rate 50
Export results to JSON
Great for diffing scans or feeding other tools.
$ node scanner.js --host 192.168.1.1 --ports 1-1024 --output results.json
Export an HTML report
A .html (or .htm) extension writes
a self-contained, styled report you can open in any browser;
any other extension writes JSON. Banner text is
HTML-escaped, so reports are safe to open even after
scanning untrusted hosts.
$ node scanner.js --host 192.168.1.1 --ports 1-1024 --output report.html
Options
| Flag | Alias | Description | Default |
|---|---|---|---|
--host |
-h |
Target hostname or IP address (required) | โ |
--ports |
-p |
Port range or comma-separated list | 1-1024 |
--protocol |
-P |
Transport protocol (tcp or
udp)
|
tcp |
--concurrency |
-c |
Max simultaneous connections | 100 |
--timeout |
-t |
Connection timeout in milliseconds | 2000 |
--rate |
-r |
Max new probes started per second | unlimited |
--output |
-o |
Export results to a file (.html โ
HTML report, otherwise JSON)
|
none |
--help |
โ | Show the help menu | โ |
Example output
Scanning 192.168.1.1 (TCP ports 1-1024)...
PORT STATE SERVICE
22/tcp open SSH (OpenSSH 8.9)
80/tcp open HTTP (nginx 1.18.0)
443/tcp open HTTPS
3306/tcp open MySQL
Scan complete: 4 open ports found in 3.2s
Programmatic API
NodeJS-PortScanner ships as an ES module, so you can import its building blocks directly โ ideal for tests and automation.
Scan ports from code
import { scanPorts } from "nodejs-portscanner";
const results = await scanPorts("127.0.0.1", [22, 80, 443], {
protocol: "tcp",
concurrency: 50,
timeout: 1500,
rate: 100,
});
for (const r of results) {
if (r.open) console.log(`${r.port}/tcp open โ ${r.service}`);
}
Parse a port spec
import { parsePorts } from "nodejs-portscanner";
parsePorts("22,80,1000-1002"); // โ [22, 80, 1000, 1001, 1002]
Fingerprint a banner
import { fingerprint } from "nodejs-portscanner";
fingerprint(22, "SSH-2.0-OpenSSH_8.9"); // โ "SSH (OpenSSH 8.9)"
fingerprint(443); // โ "HTTPS" (well-known fallback)
| Export | What it does |
|---|---|
scanPorts(host, ports, opts) |
Scan many ports concurrently; resolves to sorted results. |
scanPort(host, port, opts) |
Scan a single port and grab its banner. |
scanUdpPort(host, port, opts) |
Probe a single UDP port and classify it as open, closed, or open|filtered. |
createRateLimiter(rate) |
Build a limiter that spaces probe starts to a per-second rate. |
parsePorts(spec) |
Expand a port spec into a sorted, de-duplicated list. |
fingerprint(port, banner) |
Identify a service from its banner or well-known port. |
buildJsonReport(params) |
Produce the JSON structure used by
--output.
|
buildHtmlReport(params) |
Render a self-contained HTML report from a scan's results. |
formatTable(results) |
Render the aligned, human-readable results table. |
How it works
NodeJS-PortScanner uses Node's built-in
net.Socket to attempt TCP connections against
target ports. For each open port it:
- Establishes a connection within the configured timeout.
- Attempts banner grabbing by reading the initial server response.
- Matches the banner against a fingerprint database to identify the service.
Concurrency is managed via a bounded connection pool, so you
never exceed the configured number of simultaneous
connections or overwhelm the host's file-descriptor limits.
An optional --rate limit spaces out how many
new probes start each second to keep network noise down.
UDP scans (--protocol udp) use Node's
dgram module instead. Because UDP is
connectionless, each port is classified by the response: a
datagram reply means open, an ICMP
port-unreachable means closed, and silence
until the timeout is reported as
open|filtered (open or firewalled).
Testing
Tests run against a local mock server โ no external network calls.
$ npm test