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.
Steps to enable Suricata IPS mode with NFQUEUE:
- Confirm the installed Suricata build includes NFQUEUE support.
$ 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.
- Create a temporary directory for the local HTTP test page.
$ mkdir -p /tmp/suricata-ips-test/www
- Write a small test page for traffic that should pass.
$ printf 'Suricata IPS allowed test\n' > /tmp/suricata-ips-test/www/index.html
- Create a temporary drop rule for the blocked host header.
$ 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.
- Test the configuration with the temporary rule file loaded exclusively.
$ 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 - Create a separate log directory for the IPS test run.
$ sudo mkdir -p /var/log/suricata/ips-test
- Start the temporary HTTP server in a second terminal.
$ 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.
- Start Suricata on queue 0 before adding any queue rules.
$ 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 - Queue both directions of the local HTTP test flow.
$ 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.
- Check that the iptables rules point to queue 0.
$ 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 0For a routed gateway sensor, queue the intended FORWARD traffic instead of the loopback test port.
- Confirm that unmatched HTTP traffic still passes.
$ curl -sS --max-time 5 -H "Host: allowed.example" http://127.0.0.1:8080/ Suricata IPS allowed test
- Send HTTP traffic that matches the temporary drop rule.
$ 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.
- Read the compact alert log for the Drop action.
$ 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:8080The Drop marker proves the matching rule ran in inline IPS mode instead of passive IDS mode.
Related: How to view Suricata alert logs - Confirm the same decision in EVE JSON.
$ 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 - Remove the temporary queue rules.
$ 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
- Stop the temporary Suricata queue process.
$ sudo kill "$(cat /run/suricata-ips-test.pid)"
- Press Ctrl-C in the terminal running python3 -m http.server.
- Remove the temporary test files and logs.
$ 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
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.