Linux iptables: Rate Limit Connections per IP on Ubuntu 24.04

This guide contains practical iptables examples for limiting TCP connections, connection rates, packet rates and traffic rates per source or destination IP on Ubuntu 24.04.

Two different iptables modules are important here: connlimit limits the number of simultaneous connections, while hashlimit applies rate limits independently per source IP, destination IP or source/destination pair.

Check iptables on Ubuntu 24.04

Check the installed iptables version and backend:

sudo iptables -V

Ubuntu 24.04 normally uses the nftables backend through the iptables-nft compatibility tools. Standard iptables commands and the connlimit and hashlimit extensions can still be used with the familiar iptables syntax.

Limit Concurrent TCP Connections per Source IP

This example allows a maximum of 5 simultaneous connections per source IPv4 address. New connections above the limit are rejected:

sudo iptables -A INPUT -p tcp --syn --dport 8002 -m connlimit --connlimit-above 5 --connlimit-mask 32 -j REJECT --reject-with tcp-reset

The --connlimit-mask 32 applies the limit independently to every IPv4 source address. Using --syn is important because only new TCP connection attempts should be rejected; already established connections should not be interrupted.

Important: connlimit groups connections by source or destination address, not by TCP port. The rule itself is evaluated only for new connections to port 8002, but the connlimit counter is address-based. For a strict connection-attempt rate specifically for one service, use hashlimit as shown below.

Limit New TCP Connections per Source IP

Limit new TCP connections to port 8002 to approximately 10 connection attempts per minute for each source IP, while allowing a small burst:

sudo iptables -A INPUT -p tcp --syn --dport 8002 -m hashlimit --hashlimit-above 10/minute --hashlimit-burst 5 --hashlimit-mode srcip --hashlimit-name tcp8002_src -j REJECT --reject-with tcp-reset

Each source IP receives an independent rate bucket. Traffic below the configured rate continues through the INPUT chain, while connection attempts exceeding the limit are rejected.

Limit TCP Connection Rate per Source IP per Second

For services that receive more frequent connections, the same method can be used with a per-second limit. The following example limits each source IP to 20 new TCP connection attempts per second with a burst of 10:

sudo iptables -A INPUT -p tcp --syn --dport 443 -m hashlimit --hashlimit-above 20/second --hashlimit-burst 10 --hashlimit-mode srcip --hashlimit-name https_src -j DROP

This type of rule is useful for protecting HTTP/HTTPS services against excessive TCP connection attempts from individual clients.

Limit UDP Packets per Source IP

UDP does not have TCP-style connections, so packet rate limiting is normally more useful. This example limits traffic to UDP port 5000 to 100 packets per second for each source IP:

sudo iptables -A INPUT -p udp --dport 5000 -m hashlimit --hashlimit-above 100/second --hashlimit-burst 20 --hashlimit-mode srcip --hashlimit-name udp5000_src -j DROP

One client exceeding the configured rate is limited without affecting other source IP addresses.

Limit Traffic per Destination IP

On a Linux router, firewall or KVM hypervisor, the FORWARD chain can limit traffic independently for each destination server. This example limits new HTTPS connections to 200 per second for each destination IP:

sudo iptables -A FORWARD -p tcp --syn --dport 443 -m hashlimit --hashlimit-above 200/second --hashlimit-burst 50 --hashlimit-mode dstip --hashlimit-name https_dst -j DROP

The dstip mode creates an independent rate bucket for every destination IP passing through the server.

Limit Traffic per Source and Destination Pair

A separate rate limit can also be maintained for every source-to-destination IP pair:

sudo iptables -A FORWARD -p tcp --syn -m hashlimit --hashlimit-above 20/second --hashlimit-burst 10 --hashlimit-mode srcip,dstip --hashlimit-name src_dst_tcp -j DROP

For example, traffic from 192.0.2.10 to 203.0.113.10 gets a different rate bucket from traffic from the same source to 203.0.113.20.

Limit Connections per Source Network

Instead of applying a limit to every individual IP address, clients can be grouped by subnet. This example applies one rate limit to every /24 source network:

sudo iptables -A INPUT -p tcp --syn --dport 443 -m hashlimit --hashlimit-above 100/minute --hashlimit-burst 20 --hashlimit-mode srcip --hashlimit-srcmask 24 --hashlimit-name https_src24 -j DROP

All source addresses within the same /24 network share the same rate bucket.

Limit Traffic Bandwidth per Source IP

hashlimit can also match traffic rates in bytes per second. The following example drops incoming TCP traffic to port 8002 when one source IP exceeds approximately 1 MB/s, with an additional 2 MB burst allowance:

sudo iptables -A INPUT -p tcp --dport 8002 -m hashlimit --hashlimit-above 1mb/s --hashlimit-burst 2mb --hashlimit-mode srcip --hashlimit-name tcp8002_bw -j DROP

This is packet dropping, not traffic shaping. If smooth bandwidth control is required instead of dropping excess packets, Linux Traffic Control (tc) should be used.

Check Rate Limit Rules and Counters

Display INPUT rules together with packet and byte counters:

sudo iptables -L INPUT -n -v --line-numbers

For forwarded traffic:

sudo iptables -L FORWARD -n -v --line-numbers

The packet counters are useful for confirming that a rate-limit rule is actually matching traffic.

Check hashlimit Runtime Counters

Active hashlimit tables can also be inspected directly through procfs:

root@pc-lab:~# ls /proc/net/ipt_hashlimit/
https_dst https_src https_src24 src_dst_tcp tcp8002_bw tcp8002_src udp5000_src

For example:

cat /proc/net/ipt_hashlimit/tcp8002_src

Rule Order Is Important

iptables processes rules from top to bottom. If an ACCEPT rule for the same traffic already exists before the rate-limit rule, the rate limiter will never be reached.

Check the current rule order with:

root@pc-lab:~# sudo iptables -L INPUT -n -v --line-numbers
Chain INPUT (policy ACCEPT 189 packets, 22141 bytes)
num pkts bytes target prot opt in out source destination
1 0 0 REJECT 6 -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8002 flags:0x17/0x02 #conn src/32 > 5 reject-with tcp-reset
2 0 0 REJECT 6 -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8002 flags:0x17/0x02 limit: above 10/min burst 5 mode srcip reject-with tcp-reset
3 0 0 DROP 6 -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:443 flags:0x17/0x02 limit: above 20/sec burst 10 mode srcip
4 0 0 DROP 17 -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:5000 limit: above 100/sec burst 20 mode srcip
5 0 0 DROP 6 -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:443 flags:0x17/0x02 limit: above 100/min burst 20 mode srcip srcmask 24
6 0 0 DROP 6 -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8002 limit: above 1mb/s burst 2mb mode srcip

If required, insert a rule at a specific position instead of appending it. For example, insert a rule as INPUT rule number 3:

sudo iptables -I INPUT 3 -p tcp --syn --dport 8002 -m connlimit --connlimit-above 5 --connlimit-mask 32 -j REJECT --reject-with tcp-reset

Save iptables Rules

iptables rules created from the command line are not persistent after reboot. On Ubuntu 24.04 they can be saved using iptables-persistent:

sudo apt install -y iptables-persistent
sudo netfilter-persistent save

connlimit vs hashlimit

Use connlimit when the requirement is “maximum N simultaneous TCP connections per client”. Use hashlimit when the requirement is “maximum N packets, connection attempts or bytes per second/minute for each source or destination”.

The normal -m limit module should not be used when an independent limit per source IP is required because it provides a single shared rate limiter. Use -m hashlimit for per-IP rate limiting.