Suricata inline IPS mode puts the detection engine in the packet path so matching drop rules can stop traffic instead of only logging it. On Linux, a narrow Netfilter NFQUEUE test is the safest first step before queuing a production interface or routed gateway path.
NFQUEUE sends packets selected by iptables or nftables to a userspace queue, and Suricata reads that queue with the -q option. A temporary local HTTP listener on port 8080 keeps the first allow/block check away from production interfaces, forwarding rules, and real user traffic.
Use a maintenance window for live sensors because queued traffic depends on a running Suricata process unless the firewall rule uses a bypass option. Keep the temporary rule file, queue rules, and test logs separate from the production ruleset until both the compact alert log and EVE JSON show the drop action.
$ sudo suricata --build-info This is Suricata version 8.0.3 RELEASE Features: NFQ PCAP_SET_BUFF AF_PACKET HAVE_PACKET_FANOUT LIBCAP_NG LIBNET1.1 ##### snipped ##### Suricata Configuration: AF_PACKET support: yes ##### snipped ##### NFQueue support: yes ##### snipped #####
NFQ in the feature list and NFQueue support: yes mean the binary can attach to a Netfilter queue. Use suricata -V when only the installed version is needed.
$ mkdir -p /tmp/suricata-ips-test/www
$ printf 'Suricata IPS allowed test\n' > /tmp/suricata-ips-test/www/index.html
$ sudo vi /etc/suricata/rules/ips-test.rules
drop http any any -> any any (msg:"LOCAL IPS drop blocked.example"; http.host; content:"blocked.example"; sid:1000101; rev:1;)
Use a local sid value that does not collide with the managed ruleset. Keep this rule in a separate file until the IPS behavior is proven.
$ sudo suricata -T -c /etc/suricata/suricata.yaml -S /etc/suricata/rules/ips-test.rules -v Notice: suricata: This is Suricata version 8.0.3 RELEASE running in SYSTEM mode Info: suricata: Running suricata under test mode Info: detect: 1 rule files processed. 1 rules successfully loaded, 0 rules failed, 0 rules skipped Info: detect: 1 signatures processed. 0 are IP-only rules, 0 are inspecting packet payload, 1 inspect application layer, 0 are decoder event only Notice: suricata: Configuration provided was successfully loaded. Exiting.
-S loads the named signature file instead of the rule files listed in suricata.yaml. Use -s when the test rule should be added on top of the active ruleset.
Related: How to test Suricata configuration
$ sudo mkdir -p /var/log/suricata/ips-test
$ python3 -m http.server 8080 --bind 127.0.0.1 --directory /tmp/suricata-ips-test/www
Leave that terminal running until the cleanup step. The listener stays on loopback and is used only for the allow/block check.
$ sudo suricata -c /etc/suricata/suricata.yaml -S /etc/suricata/rules/ips-test.rules -q 0 -l /var/log/suricata/ips-test -D --pidfile /run/suricata-ips-test.pid i: suricata: This is Suricata version 8.0.3 RELEASE running in SYSTEM mode
-q 0 attaches Suricata to queue 0. Starting the engine first avoids sending packets to an unwatched queue.
Related: How to manage the Suricata service
$ sudo iptables -I OUTPUT -p tcp --dport 8080 -j NFQUEUE --queue-num 0 $ sudo iptables -I INPUT -p tcp --sport 8080 -j NFQUEUE --queue-num 0
Keep the first queue rule narrow. Broad INPUT, OUTPUT, or FORWARD queue rules can interrupt traffic if Suricata is stopped or misconfigured.
$ sudo iptables -vnL OUTPUT
Chain OUTPUT (policy ACCEPT 20 packets, 1772 bytes)
pkts bytes target prot opt in out source destination
0 0 NFQUEUE tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 NFQUEUE num 0
$ sudo iptables -vnL INPUT
Chain INPUT (policy ACCEPT 11 packets, 734 bytes)
pkts bytes target prot opt in out source destination
0 0 NFQUEUE tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp spt:8080 NFQUEUE num 0
For a routed gateway sensor, queue the intended FORWARD traffic instead of the loopback test port.
$ curl -sS --max-time 5 -H "Host: allowed.example" http://127.0.0.1:8080/ Suricata IPS allowed test
$ curl -sS --max-time 5 -H "Host: blocked.example" http://127.0.0.1:8080/ curl: (28) Operation timed out after 5008 milliseconds with 0 bytes received
The timeout occurs because the matching HTTP request was dropped before the local server returned a response.
$ sudo cat /var/log/suricata/ips-test/fast.log
06/25/2026-07:42:56.827020 [Drop] [**] [1:1000101:1] LOCAL IPS drop blocked.example [**] [Classification: (null)] [Priority: 3] {TCP} 127.0.0.1:35338 -> 127.0.0.1:8080
The Drop marker proves the matching rule ran in inline IPS mode instead of passive IDS mode.
Related: How to view Suricata alert logs
$ sudo jq 'select(.event_type=="alert") | {event_type, action: .alert.action, signature_id: .alert.signature_id, signature: .alert.signature, proto, src_ip, dest_ip}' /var/log/suricata/ips-test/eve.json
{
"event_type": "alert",
"action": "blocked",
"signature_id": 1000101,
"signature": "LOCAL IPS drop blocked.example",
"proto": "TCP",
"src_ip": "127.0.0.1",
"dest_ip": "127.0.0.1"
}
action: blocked is the structured proof that Suricata returned a blocking verdict for the packet.
Related: How to read Suricata eve.json logs
$ sudo iptables -D OUTPUT -p tcp --dport 8080 -j NFQUEUE --queue-num 0 $ sudo iptables -D INPUT -p tcp --sport 8080 -j NFQUEUE --queue-num 0
$ sudo kill "$(cat /run/suricata-ips-test.pid)"
$ sudo rm -f /run/suricata-ips-test.pid $ sudo rm -f /etc/suricata/rules/ips-test.rules $ rm -rf /tmp/suricata-ips-test $ sudo rm -rf /var/log/suricata/ips-test