Wireshark gives you two completely independent filtering mechanisms. They look similar on the surface — both let you write filter expressions to isolate traffic — but they operate at different stages of the capture pipeline, use entirely different syntax engines, and serve different purposes.
Runs in the kernel via libpcap / Npcap. Packets that don't match are never written to the capture buffer. Low overhead, limited syntax.
Applied inside Wireshark's dissector engine after packets are captured. All packets are kept; non-matching ones are merely hidden. Rich, protocol-aware syntax.
Understanding this distinction is the most important conceptual leap for anyone moving from casual to serious packet analysis.
Capture filters use Berkeley Packet Filter (BPF) syntax — the same language used by tcpdump. They are compiled into kernel bytecode by libpcap and evaluated in the kernel's network stack before a packet ever reaches userspace.
# Capture only traffic to/from a specific host
host 192.168.1.100
# Only traffic from a source IP
src host 10.0.0.5
# Only traffic to a destination IP
dst host 10.0.0.5
# Capture an entire subnet
net 192.168.1.0/24
# Only TCP traffic
tcp
# Only UDP traffic
udp
# Only ICMP (ping) packets
icmp
# Only ARP packets
arp
# Capture only HTTP traffic (port 80)
tcp port 80
# HTTPS traffic
tcp port 443
# DNS queries
udp port 53
# A port range (e.g., all registered ports)
tcp portrange 1024-65535
# TCP on port 80 OR 443
tcp port 80 or tcp port 443
# Traffic from 192.168.1.5 that is NOT DNS
src host 192.168.1.5 and not udp port 53
# Entire subnet excluding a noisy broadcast address
net 10.0.0.0/8 and not host 10.255.255.255
# SYN packets only (TCP flag bit 0x02 set)
tcp[13] == 0x02
Display filters use Wireshark's own Display Filter Language (DFL) — a rich, protocol-aware expression engine that understands every field in every dissector Wireshark supports. You can filter on deeply nested protocol fields, compare values, and even match on byte sequences.
// Show only HTTP packets
http
// Show only DNS packets
dns
// Show only TLS (HTTPS handshakes, etc.)
tls
// Show only ARP packets
arp
// Traffic to or from an IP
ip.addr == 192.168.1.100
// Only from a source
ip.src == 10.0.0.5
// Only to a destination
ip.dst == 10.0.0.5
// Entire subnet using CIDR
ip.addr == 192.168.1.0/24
// Exclude your own machine's loopback
!ip.addr == 127.0.0.1
// HTTP on port 80
tcp.port == 80
// HTTPS
tcp.port == 443
// DNS (UDP)
udp.port == 53
// Source port greater than 1024 (ephemeral client port)
tcp.srcport > 1024
// Port range using range operator
tcp.port in {80 443 8080 8443}
// Only SYN packets (TCP handshake initiators)
tcp.flags.syn == 1 and tcp.flags.ack == 0
// SYN-ACK (server response in 3-way handshake)
tcp.flags.syn == 1 and tcp.flags.ack == 1
// RST packets — connection resets
tcp.flags.reset == 1
// FIN packets — graceful teardown
tcp.flags.fin == 1
// Only HTTP GET requests
http.request.method == "GET"
// HTTP POST requests
http.request.method == "POST"
// HTTP 4xx client errors
http.response.code >= 400 and http.response.code < 500
// Filter by URL containing a keyword
http.request.uri contains "login"
// Filter by Host header
http.host == "example.com"
// DNS queries only (QR bit = 0)
dns.flags.response == 0
// DNS responses only (QR bit = 1)
dns.flags.response == 1
// DNS queries for a specific domain
dns.qry.name contains "example.com"
// NXDOMAIN responses (non-existent domain)
dns.flags.rcode == 3
// Case-sensitive substring match
frame contains "password"
// Regular expression match (Perl-compatible)
http.request.uri matches "\\.(php|asp|aspx)$"
// Match on raw bytes (hex)
frame[0:2] == 0xff:0xd8 // JPEG magic bytes
| Property | Capture Filter | Display Filter |
|---|---|---|
| When applied | Before packet is stored | After packet is captured |
| Syntax engine | BPF (Berkeley Packet Filter) | Wireshark DFL |
| Execution layer | Kernel (libpcap / Npcap) | Userspace (Wireshark dissectors) |
| Change during capture | ❌ No | ✅ Yes, anytime |
| Drops packets | ✅ Permanently | ❌ Just hides them |
| Protocol awareness | Layer 3/4 only | All layers, all dissectors |
| Performance impact | Very low (kernel JIT) | Higher (userspace eval) |
| Regex support | ❌ No | ✅ Yes (matches) |
Field access (e.g., http.host) |
❌ Not available | ✅ Full field tree |
| tcpdump compatible | ✅ Yes | ❌ No |
The most powerful workflows combine a broad capture filter to keep the capture file manageable with precise display filters to zero in on specific packets during analysis.
# Only capture traffic to/from the web server
host 203.0.113.42 and (tcp port 80 or tcp port 443)
// First pass: find all 5xx server errors
http.response.code >= 500
// Second pass: isolate slow requests (large TCP window scaling)
tcp.analysis.ack_rtt > 0.3
// Third pass: look for suspicious POST bodies
http.request.method == "POST" and frame contains "eval("
# CAPTURE FILTER — narrow to the troubled subnet
net 172.16.0.0/16 and not udp port 5353 # exclude mDNS noise
── after capture, switch to display filters ──
// Find TCP retransmissions
tcp.analysis.retransmission
// Find duplicate ACKs (indicator of loss)
tcp.analysis.duplicate_ack
// Packets with TTL under 5 (near expiry — routing loop?)
ip.ttl < 5
// Zero-window — receiver buffer full
tcp.analysis.zero_window
# This will FAIL — ip.addr is a DFL field, not valid BPF
ip.addr == 192.168.1.1 ← invalid in capture filter bar
host 192.168.1.1
ip.addr// Intended: exclude all traffic to/from 10.0.0.1
!(ip.addr == 10.0.0.1)
// PROBLEM: ip.addr matches BOTH src and dst.
// A packet FROM 10.0.0.1 → 8.8.8.8 still passes
// because ip.dst != 10.0.0.1 is true.
// ✅ Correct approach:
!ip.src == 10.0.0.1 and !ip.dst == 10.0.0.1