Netcat Cheatsheet
Port Scanning
Use this Netcat reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Zero-I/O mode
-z connects, reports, and closes without transferring data. Combined with
-v it is a usable port check, and it is the only scanning netcat does.
nc -zv 10.0.0.5 22 nc -zv 10.0.0.5 20-25 nc -zv 10.0.0.5 22 80 443 nc -zvn -w 1 10.0.0.5 1-1024
| Flag | Why |
|---|---|
-z | Zero I/O, do not send or expect data |
-v | Required. Without it, results are printed nowhere |
-n | No DNS, otherwise every port waits on a lookup |
-w 1 | Per-port timeout. Without it, filtered ports take minutes |
-r | Randomize the port order (not in Ncat) |
-u | Scan UDP instead, but see the caveat below |
Ranges are lo-hi inclusive, and a space-separated list works on OpenBSD
nc and macOS nc. Output goes to stderr, which is why this looks empty:
nc -zv 10.0.0.5 1-100 | grep succeeded # WRONG, nothing matches nc -zv 10.0.0.5 1-100 2>&1 | grep succeeded # right
Reading the results
Connection to 10.0.0.5 22 port [tcp/ssh] succeeded! nc: connectx to 10.0.0.5 port 23 (tcp) failed: Connection refused nc: connectx to 10.0.0.5 port 445 (tcp) failed: Operation timed out
| Result | State | Means |
|---|---|---|
succeeded! | open | Handshake completed, something is listening |
Connection refused | closed | Host reachable, a RST came back |
Times out at -w | filtered | Silently dropped by a firewall |
No route to host | — | Local routing problem, not the target |
| ICMP unreachable | filtered | A router refused explicitly |
The distinction that matters: refused proves the host is up, filtered tells you nothing about the host. A scan that is all timeouts usually means the host is down or you are being blocked wholesale.
Scripting a scan
# Open ports only, one per line nc -zvn -w 1 10.0.0.5 1-1024 2>&1 | grep succeeded # Just the port numbers nc -zvn -w 1 10.0.0.5 1-1024 2>&1 | \ awk '/succeeded/ {print $(NF-2)}' # Common ports across a subnet for h in 10.0.0.{1..254}; do for p in 22 80 443 3389; do nc -zn -w 1 "$h" "$p" 2>/dev/null && echo "$h:$p open" done done # Parallel, much faster on a /24 printf '10.0.0.%s\n' {1..254} | xargs -P 50 -I{} sh -c \ 'nc -zn -w 1 {} 22 2>/dev/null && echo "{} 22 open"' # Single check for a script or healthcheck nc -z -w 3 db.internal 5432 && echo up || echo down
The awk field index depends on the variant's message format, so check the
raw output once before trusting a parse. grep succeeded is portable.
Waiting for a port to come up
The most legitimately common use of netcat in automation, worth more than the scanning.
# Wait for Postgres, give up after 30 tries for i in $(seq 30); do nc -z -w 1 db 5432 && break sleep 1 done # Compact form until nc -z -w 1 db 5432; do sleep 1; done echo "db is accepting connections" # With a hard deadline timeout 60 sh -c 'until nc -z -w 1 db 5432; do sleep 1; done' \ || { echo "db never came up"; exit 1; }
This is the entire content of most wait-for-it.sh scripts. Remember that
-z success means the port accepts TCP, not that the service finished
initializing. Postgres binds its port before it is ready to answer queries,
so follow up with a real protocol check where it matters.
UDP "scanning" mostly does not work
nc -zvu 10.0.0.5 53 nc -zvu -w 1 10.0.0.5 100-200
UDP has no handshake, so there is nothing to succeed. What you actually learn:
| Observed | Inferred |
|---|---|
| ICMP port unreachable | closed, and only if not rate-limited |
| Nothing at all | open or filtered or the packet was lost |
| A reply payload | open, and now you know the service |
nc -zu reports "succeeded" for almost every port because the local send
succeeded. It is not a UDP port check. Send a real payload for the
protocol instead:
# A DNS query is the only reliable way to check port 53 dig +short +time=2 @10.0.0.5 example.com # NTP, SNMP, and similar need their own probe ntpdate -q 10.0.0.5
Linux rate-limits ICMP unreachables (roughly one per second by default), so even the closed-port signal is unreliable across a range.
When to reach for nmap instead
netcat's scan is a full TCP connect on every port, sequentially, which is slow and lands in every log on the target.
| Need | Tool |
|---|---|
| One port, in a script | nc -z -w 3 |
| Wait for a service | until nc -z ...; do sleep 1; done |
| A range, quickly | nmap -p 1-1024 10.0.0.5 |
| Service and version | nmap -sV -p 22,80,443 10.0.0.5 |
| SYN scan, no full handshake | sudo nmap -sS 10.0.0.5 |
| Real UDP scan | sudo nmap -sU -p 53,161 10.0.0.5 |
| Host discovery on a subnet | nmap -sn 10.0.0.0/24 |
# nmap does in one second what the netcat loop does in minutes nmap -Pn -p 1-1024 --open 10.0.0.5
Scan only hosts you own or have written authorization to test. Unauthorized port scanning is a crime in many jurisdictions, and a full connect scan is trivially attributable because it completes the handshake from your address.