tcpdump is a command-line packet analyzer built on top of libpcap. It captures the raw packets that pass through a network interface and prints them — or writes them to a .pcap file — so you can answer questions like "is my server actually receiving this request?", "which side closed the connection?", or "why is TLS failing on port 443?".
It is small, scriptable, available on every modern Unix, and the standard tool reached for whenever a higher-level diagnostic (curl, ping, application logs) does not give a clear answer.
This guide walks through installation on Linux, macOS, and Windows, the flags you will actually use day-to-day, the BPF filter syntax, and a handful of recipes for common server-side investigations.
Installation
Debian / Ubuntu
sudo apt update
sudo apt install -y tcpdump
RHEL / CentOS / AlmaLinux / Rocky / Fedora
# RHEL 8/9, AlmaLinux, Rocky, Fedora
sudo dnf install -y tcpdump
# Older CentOS / RHEL 7
sudo yum install -y tcpdump
Arch Linux
sudo pacman -S tcpdump
Alpine
sudo apk add tcpdump
Verify the install
tcpdump --version
You should see the tcpdump and libpcap versions printed.
macOS
macOS ships tcpdump out of the box, but the bundled version trails upstream. To get the latest release, install via Homebrew:
brew install tcpdump
Then either run /opt/homebrew/bin/tcpdump directly or make sure /opt/homebrew/bin (Apple Silicon) or /usr/local/bin (Intel) is ahead of /usr/sbin in your PATH.
On macOS you will also need to capture as root (sudo tcpdump …) — the same as on Linux.
Windows
tcpdump does not run natively on Windows. You have three realistic options:
- Wireshark +
tshark(recommended). Install Wireshark, which bundles Npcap (the modern packet-capture driver) and the command-line tooltshark.tsharkaccepts the same BPF filter syntax astcpdumpand is the closest equivalent on Windows.# Capture on the first interface, write to file tshark -i 1 -w C:\captures\out.pcapng # Capture HTTP traffic to/from a specific host tshark -i 1 -f "host 10.0.0.5 and tcp port 80" - WSL2. Install
tcpdumpinside an Ubuntu/Debian WSL distribution. Note that in WSL2 you are capturing on the virtualized WSL network adapter, not the Windows host's physical NIC, so this is best for capturing traffic generated by Linux processes inside WSL — not arbitrary Windows traffic. - WinDump (legacy, not recommended). A historical Windows port of
tcpdump. The last release dates from 2006 and depends on the deprecated WinPcap driver. Avoid for new work.
For server-grade capture on Windows, prefer tshark.
Permissions
Packet capture requires raw socket access, which on every supported platform means root (or Administrator on Windows).
On Linux you can grant the binary the necessary capability so an unprivileged user can capture, instead of using sudo everywhere:
sudo setcap cap_net_raw,cap_net_admin=eip $(which tcpdump)
This is convenient on shared boxes but be aware it broadens who can capture traffic.
Listing interfaces
Before capturing, find the interface you want to listen on:
sudo tcpdump -D
You will see something like:
1.eth0 [Up, Running]
2.lo [Up, Running, Loopback]
3.any (Pseudo-device that captures on all interfaces) [Up, Running]
The pseudo-interface any is useful when you do not yet know which NIC the traffic is on.
A first capture
sudo tcpdump -i eth0
That prints every packet seen on eth0 until you press Ctrl+C. On a busy server this is unreadable — almost every real-world invocation adds filters and flags to narrow it down.
The flags you will actually use
| Flag | Meaning |
|---|---|
-i <iface> |
Interface to listen on. Use any for all interfaces. |
-n |
Do not resolve IPs to hostnames. Always use this unless you specifically want DNS lookups. |
-nn |
Also do not resolve port numbers to service names. |
-c <count> |
Stop after capturing count packets. |
-w <file> |
Write raw packets to a .pcap file instead of decoding to stdout. |
-r <file> |
Read packets back from a .pcap file. |
-s <snaplen> |
Snapshot length per packet. -s 0 means "full packet" (the modern default). |
-v / -vv / -vvv |
Increase verbosity of the decoded output. |
-A |
Print packet payload in ASCII. Useful for plain-text protocols (HTTP, SMTP, IMAP). |
-X |
Print packet payload in hex + ASCII. |
-e |
Print the link-layer (Ethernet) header, including MAC addresses. |
-tttt |
Use absolute, human-readable timestamps. |
-q |
Quick output — one line per packet, less detail. |
-G <secs> |
Rotate the output file every secs seconds (used with -w). |
-W <count> |
Limit the number of rotated files (-G/-C). |
-C <MB> |
Rotate the output file when it reaches MB megabytes. |
-Z <user> |
Drop privileges to <user> after opening the interface. |
A safe, readable default for most investigations:
sudo tcpdump -i any -nn -tttt -s 0
BPF filters: the part that matters
tcpdump accepts a Berkeley Packet Filter (BPF) expression as its last argument. The kernel applies the filter before the packet reaches userspace, so filtering is cheap even on busy interfaces.
Filter by host
# Anything to or from 10.0.0.5
sudo tcpdump -i any -nn host 10.0.0.5
# Only traffic FROM that host
sudo tcpdump -i any -nn src host 10.0.0.5
# Only traffic TO that host
sudo tcpdump -i any -nn dst host 10.0.0.5
Filter by port
# Anything to or from port 443 (TLS)
sudo tcpdump -i any -nn port 443
# Only HTTP server traffic (incoming requests)
sudo tcpdump -i any -nn dst port 80
# Port range
sudo tcpdump -i any -nn portrange 8000-8100
Filter by protocol
sudo tcpdump -i any -nn icmp # pings
sudo tcpdump -i any -nn udp # all UDP
sudo tcpdump -i any -nn 'tcp and port 22' # SSH
sudo tcpdump -i any -nn arp # ARP requests/replies
Filter by network
sudo tcpdump -i any -nn net 10.0.0.0/24
sudo tcpdump -i any -nn 'src net 192.168.1.0/24 and dst port 443'
Combining with and / or / not
# DB traffic, but ignore the monitoring agent's IP
sudo tcpdump -i any -nn 'tcp port 5432 and not host 10.0.0.99'
# DNS in either direction over UDP or TCP
sudo tcpdump -i any -nn 'port 53 and (udp or tcp)'
Always quote a filter expression when it contains spaces, parentheses, or shell metacharacters.
TCP flag filters (advanced)
To catch only TCP SYN packets (handy when a service is silently dropping connections):
sudo tcpdump -i any -nn 'tcp[tcpflags] & tcp-syn != 0 and not tcp[tcpflags] & tcp-ack != 0'
Or only RSTs (often the fingerprint of a misbehaving service or firewall):
sudo tcpdump -i any -nn 'tcp[tcpflags] & tcp-rst != 0'
Saving and reading captures
For anything non-trivial, write to a file and analyze later — printing to a terminal can drop packets on a busy host.
# Capture HTTPS traffic to a customer's IP, full packet, into a file
sudo tcpdump -i any -nn -s 0 -w /tmp/customer.pcap 'host 203.0.113.10 and tcp port 443'
Read it back without re-capturing:
tcpdump -nn -r /tmp/customer.pcap
# Or apply an additional filter while reading
tcpdump -nn -r /tmp/customer.pcap 'src host 203.0.113.10'
Open it in Wireshark on your laptop for a richer view:
scp server:/tmp/customer.pcap .
wireshark customer.pcap
Rotating large captures
For long-running diagnostics, rotate so you do not fill the disk:
# Rotate every 60 seconds, keep at most 10 files
sudo tcpdump -i any -nn -s 0 -w /var/log/cap-%Y%m%d-%H%M%S.pcap -G 60 -W 10
# Or rotate by size — every 100 MB, keep 5 files
sudo tcpdump -i any -nn -s 0 -w /var/log/cap.pcap -C 100 -W 5
%Y%m%d-%H%M%S is a strftime pattern; tcpdump expands it for each rotated file.
Practical recipes
Is the server even seeing the request?
sudo tcpdump -i any -nn 'host 198.51.100.7 and tcp port 443'
If you see SYNs but no SYN/ACK, the firewall or the listening service is dropping them.
Watch a TLS handshake without the encrypted bits
sudo tcpdump -i any -nn -X 'tcp port 443' | head -200
You will see ClientHello / ServerHello in the payload header bytes — enough to confirm a handshake is being attempted.
See raw HTTP requests/responses
sudo tcpdump -i any -nn -A -s 0 'tcp port 80'
Useful for plain HTTP. Useless for HTTPS — that traffic is encrypted.
Capture DNS queries from the server
sudo tcpdump -i any -nn 'port 53'
Combined with -vv you get the queried name and reply for each transaction.
Confirm an IP is reaching the box on a specific port
sudo tcpdump -i any -nn -c 20 'src host 198.51.100.7 and dst port 22'
-c 20 stops after 20 matching packets — a quick smoke test.
Diagnose connection resets
sudo tcpdump -i any -nn 'tcp[tcpflags] & tcp-rst != 0'
Identifies who is sending RSTs — useful when an application reports "connection reset by peer" but does not say which side.
Reading the output
A typical line looks like:
14:02:11.183204 IP 10.0.0.5.52344 > 10.0.0.10.443: Flags [S], seq 2746183, win 64240, options [mss 1460,sackOK,TS val 1234 ecr 0,nop,wscale 7], length 0
Broken down:
14:02:11.183204— timestamp.IP 10.0.0.5.52344 > 10.0.0.10.443— sourceIP.portto destinationIP.port.Flags [S]— TCP flags.S= SYN,.= ACK only,P= PSH,F= FIN,R= RST.seq 2746183— sequence number.win 64240— receive window size.length 0— payload length (0 for a SYN).
A complete TCP handshake will appear as [S] → [S.] → [.], then payload exchange, then [F.] / [F.] / [.] to close (or [R] if it was reset).
Operational tips
- Always include
-nn. DNS lookups during capture turn a fast, focused trace into a slow, noisy one. - Always filter. An unfiltered
tcpdumpon a production interface can drop packets and adds CPU load. Be specific about hosts/ports. - Mind the disk.
-wplus a busy interface fills disks quickly. Use-C/-G/-Wfor long captures. - Sensitive data is in there. Capture files contain real payloads — credentials, cookies, PII. Treat
.pcapfiles as production secrets and delete them when done. - Drop privileges.
tcpdump -Z nobodyruns asnobodyafter opening the socket, limiting damage if the parser ever has a bug. - One terminal per filter. When debugging two flows at once, run two
tcpdumpinstances in two terminals with different filters rather than one giant filter — easier to read. - Switch to
tshark/ Wireshark for deep dives.tcpdumpis for triage. Once you have a.pcap, Wireshark gives you protocol decoders, flow graphs, and Follow-Stream — far more useful for reading application-level conversations.
Troubleshooting
tcpdump: <iface>: You don't have permission to capture on that device— run withsudo, or grant capabilities once withsetcap cap_net_raw,cap_net_admin=eip $(which tcpdump).- No packets at all, but you know the server is being hit — you may be on the wrong interface (try
-i any), or the traffic is being rerouted through a tunnel/bridge with a different name (ip link show). - Lots of
unknownor truncated output — your snaplen is too small. Use-s 0. - Dropped packets reported on exit — the interface is too busy for live decoding. Switch to
-w file.pcapand analyze offline. tcpdumpdecodes asUnknownfor a known protocol — likely a non-standard port. Tell tcpdump explicitly, e.g.tcpdump -i any -nn 'tcp port 8443' -X.
Summary
tcpdump is one of the highest-leverage tools a server admin can have on hand. The minimum viable mental model is small:
- Pick an interface (
-i anyif unsure). - Add
-nnto suppress name resolution. - Write a BPF filter that narrows to the host/port/protocol you care about.
- Either watch live, or
-wto a.pcapand open it in Wireshark.
From there, the recipes above cover most real incidents: confirming a request reaches the box, diagnosing TLS or DNS, hunting RSTs, and snapshotting traffic for offline analysis.