When you need to know what’s really crossing the wire, whether requests reach the server, whether replies go out, or why a connection resets, tcpdump gives definitive answers. It captures packets on an interface, filters them, and either prints them or saves them for Wireshark.
Install and basics
sudo apt install tcpdump # Debian/Ubuntu
sudo dnf install tcpdump # RHEL family
ip -br link # find your interface name (eth0, ens3, enp1s0…)
sudo tcpdump -i eth0 # capture everything (Ctrl+C to stop)
sudo tcpdump -i any # all interfaces
Recommended defaults:
sudo tcpdump -i eth0 -nn -c 100
-nn— don’t resolve hostnames or port names. It’s faster and avoids generating DNS traffic.-c 100— stop after 100 packets.-v,-vv— more detail.-A— print payload as ASCII.-Xprints hex plus ASCII.
Filters (BPF syntax)
Filtering is what makes tcpdump useful on a busy server. Exclude your own SSH session or you’ll mostly capture yourself:
sudo tcpdump -i eth0 -nn not port 22
Common filters:
host 203.0.113.50 # to or from an IP
src host 203.0.113.50 # only from
dst port 443 # only to port 443
port 80 or port 443
tcp port 3306 # MySQL
udp port 53 # DNS
net 10.0.0.0/24 # a subnet
icmp # ping
'tcp[tcpflags] & (tcp-syn) != 0' # SYN packets (new connections)
'tcp[tcpflags] & (tcp-rst) != 0' # resets
Combine with and, or, not and parentheses (quote the expression):
sudo tcpdump -i eth0 -nn 'host 203.0.113.50 and (port 80 or port 443)'
Reading the output
14:02:11.483920 IP 198.51.100.7.53412 > 203.0.113.10.443: Flags [S], seq 1234…, win 64240, length 0
14:02:11.483961 IP 203.0.113.10.443 > 198.51.100.7.53412: Flags [S.], seq 9876…, ack 1235, length 0
14:02:11.512305 IP 198.51.100.7.53412 > 203.0.113.10.443: Flags [.], ack 1, length 0
That’s the TCP handshake: SYN, SYN-ACK, ACK. TCP flags:
| Flag | Meaning |
|---|---|
[S] |
SYN: connection request |
[S.] |
SYN-ACK: server accepts |
[.] |
ACK |
[P.] |
Push: data |
[F.] |
FIN: close |
[R] / [R.] |
RST: reset or refused |
Debugging recipes
“Can’t connect to my service”
sudo tcpdump -i any -nn port 8080
Then try connecting from outside:
- No packets at all → traffic doesn’t reach the server: DNS points elsewhere, an upstream or provider firewall, or the wrong IP.
- SYN arrives, no reply → a local firewall drops it (UFW/nftables). See check open ports.
- SYN arrives, RST goes back → nothing is listening on that port or address (
ss -tlnp). The app may be bound to 127.0.0.1 only. See ss command. - Handshake completes, then a reset → the app closes the connection. Check app logs and TLS mismatches.
DNS queries
sudo tcpdump -i any -nn udp port 53
This shows which resolver the server asks, what it asks for, and whether answers come back.
HTTP traffic (unencrypted)
sudo tcpdump -i lo -nn -A -s 0 'tcp port 3000 and (tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420)'
That prints GET requests to a local app on port 3000. Traffic between Nginx and a backend on localhost is plain HTTP and easy to inspect on lo. External HTTPS is encrypted, so you only see TLS records, not content.
Is someone flooding the server?
sudo tcpdump -i eth0 -nn -c 2000 not port 22 | awk '{print $3}' | cut -d. -f1-4 | sort | uniq -c | sort -rn | head
This counts packets per source IP in a quick sample. See sort and uniq.
Save to a file for Wireshark
sudo tcpdump -i eth0 -nn -s 0 -w /tmp/capture.pcap 'port 443'
Copy it to your computer (scp) and open it in Wireshark for deep analysis: TCP streams, retransmissions, TLS handshakes.
Rotating capture files for long-running captures:
sudo tcpdump -i eth0 -w /tmp/cap-%H%M.pcap -G 300 -W 12 'port 5060'
That gives a new file every 5 minutes and keeps 12 of them.
Read a capture back with tcpdump -nn -r /tmp/capture.pcap.
Cautions
- Captures can contain sensitive data: cookies, passwords in plain protocols, personal data. Limit the filter, delete files afterwards, and respect your privacy obligations.
- On busy interfaces, unfiltered captures grow fast and can drop packets. Filter tightly and use
-cor-G/-W. - tcpdump sees traffic before local firewall rules on input, and after them on output. A packet visible in tcpdump can still be dropped by iptables or nftables afterwards.
Quick reference
| Task | Command |
|---|---|
| Capture on interface | tcpdump -i eth0 -nn |
| Exclude SSH | … not port 22 |
| By host and port | … host 1.2.3.4 and port 443 |
| Only new connections | … 'tcp[tcpflags] & tcp-syn != 0' |
| Show payload | -A / -X |
| Save | -w file.pcap |
| Read | -r file.pcap |
| Limit count | -c 100 |
Related: ping and mtr and curl.