Project

General

Profile

Actions

Bug #8377

open

OPNsense Suricata divert-to mode causes aborted HTTPS response streams (Axios / Node.js) – works with responseType: stream

Added by ABC DEF 1 day ago. Updated about 14 hours ago.

Status:
New
Priority:
Normal
Assignee:
-
Target version:
Affected Versions:
Effort:
Difficulty:
Label:

Description

Describe the bug

When using Suricata with the new divert-to mode in OPNsense, some HTTPS requests made with Node.js (Axios) fail with response stream aborted.

The same request works normally when:

  • `divert-to` is disabled (`none`)
  • Axios is configured with `responseType: "stream"`
  • requesting a static file (e.g. `/robots.txt`) instead of an HTML page

Issue in OPNsense: https://github.com/opnsense/core/issues/9956

Network Setup

Cloud VM (uptime-kuma)

Internet

OPNsense (Suricata IPS enabled, divert-to mode)

Reverse Proxy

Backend Application

To Reproduce

The request is sent from an external cloud VM to a public service behind OPNsense.

  1. Deploy a service behind OPNsense that is reachable via HTTPS through a reverse proxy.
  2. Enable Suricata IPS on the WAN interface using divert-to mode.
  3. From an external system (e.g. cloud VM), run the following Node.js request:
node -e ' const axios = require("axios"); const https = require("https"); const http = require("http"); const httpsAgent = new https.Agent({ rejectUnauthorized: true, maxCachedSessions: 0 }); const httpAgent = new http.Agent({ maxCachedSessions: 0 }); axios({ url: "https://example.com", method: "get", timeout: 10000, maxRedirects: 10, httpAgent, httpsAgent, headers: { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" } }) .then(res => { console.log("STATUS", res.status); }) .catch(err => { console.error("ERROR", err.message); }); '

Output: ERROR response stream aborted

Suricata disabled

When i set divert-to to None it works as expected:

node -e ' const axios = require("axios"); const https = require("https"); const http = require("http"); const httpsAgent = new https.Agent({ rejectUnauthorized: true, maxCachedSessions: 0 }); const httpAgent = new http.Agent({ maxCachedSessions: 0 }); axios({ url: "https://example.com", method: "get", timeout: 10000, maxRedirects: 10, httpAgent, httpsAgent, headers: { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" } }) .then(res => { console.log("STATUS", res.status); }) .catch(err => { console.error("ERROR", err.message); }); '

Output: STATUS 200

Response Type: stream

If set 'responseType: "stream",' the request works as expected even with suricata divert-to enabled:

node -e ' const axios = require("axios"); const https = require("https"); const http = require("http"); const httpsAgent = new https.Agent({ rejectUnauthorized: true, maxCachedSessions: 0 }); const httpAgent = new http.Agent({ maxCachedSessions: 0 }); axios({ url: "https://example.com", method: "get", responseType: "stream", timeout: 10000, maxRedirects: 10, httpAgent, httpsAgent, headers: { "A
ccept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" } }) .then(res => { console.log("STATUS", res.status); }) .catch(err => { console.error("ERROR", err.message); }); '

Output: STATUS 200

Expected behavior

I would expect STATUS 200 with suricata divert-to enabled.

Relevant log files

There are no relevant logs and no alerts.

Environment

  • OPNsense version: 26.1.4
  • Suricata version: 8.0.3
  • Client: Node.js + Axios
  • Test system: containerized uptime-kuma
Actions #1

Updated by Victor Julien 1 day ago

Can you share any Suricata logs generated for this traffic? Would also be good record a pcap to see if that gives any clues.

Actions #2

Updated by ABC DEF about 15 hours ago

Hello Victor,

I sent you the requested packet captures via email (I don’t want to share them publicly). I’m not sure which Suricata logs might be helpful. As far as I could see when looking through them, there wasn’t really anything that stood out or seems related to this request.

Actions #3

Updated by ABC DEF about 14 hours ago

This issue only occurs when divert-to is used on a WAN-side pf rule, i.e., when the passing traffic is still encrypted.
If I add the divert-to setting to the rule from the reverse proxy to the web server (where traffic is unencrypted) instead, the request works correctly and the eve logging shows:

{"timestamp":"2026-03-14T09:47:41.161486+0100","flow_id":1411451134062175,"event_type":"http","src_ip":"ip.from.reverse.proxy","src_port":54814,"dest_ip":"ip.from.web.server","dest_port":8080,"proto":"TCP","ip_v":4,"pkt_src":"wire/pcap","tx_id":0,"http":{"hostname":"example.com","url":"/","http_user_agent":"axios/0.30.3","xff":"::ffff:ip.from.requesting.host","http_content_type":"text/html","http_method":"GET","protocol":"HTTP/1.1","status":200,"length":123929,"request_headers":[{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"},{"name":"user-agent","value":"axios/0.30.3"},{"name":"host","value":"example.com"},{"name":"x-forwarded-proto","value":"https"},{"name":"x-forwarded-for","value":"::ffff:ip.from.requesting.host"}],"response_headers":[{"name":"Date","value":"Sat, 14 Mar 2026 08:47:41 GMT"},{"name":"Server","value":"Apache/2.4.66 (Debian)"},{"name":"X-Powered-By","value":"PHP/8.3.30"},{"name":"Link","value":"<https://example.com/wp-json/>; rel=\"https://api.w.org/\""},{"name":"Vary","value":"Accept-Encoding"},{"name":"Transfer-Encoding","value":"chunked"},{"name":"Content-Type","value":"text/html; charset=UTF-8"}]}}
{"timestamp":"2026-03-14T09:47:48.431785+0100","flow_id":1133209534922269,"event_type":"http","src_ip":"ip.from.reverse.proxy","src_port":35892,"dest_ip":"ip.from.web.server","dest_port":8080,"proto":"TCP","ip_v":4,"pkt_src":"wire/pcap","tx_id":0,"http":{"hostname":"example.com","url":"/robots.txt","http_user_agent":"Uptime-Kuma/2.2.1","xff":"::ffff:ip.from.requesting.host","http_content_type":"text/plain","http_method":"GET","protocol":"HTTP/1.1","status":200,"length":168,"request_headers":[{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"},{"name":"user-agent","value":"Uptime-Kuma/2.2.1"},{"name":"host","value":"example.com"},{"name":"x-forwarded-proto","value":"https"},{"name":"x-forwarded-for","value":"::ffff:ip.from.requesting.host"}],"response_headers":[{"name":"Date","value":"Sat, 14 Mar 2026 08:47:48 GMT"},{"name":"Server","value":"Apache/2.4.66 (Debian)"},{"name":"X-Powered-By","value":"PHP/8.3.30"},{"name":"X-Robots-Tag","value":"noindex, follow"},{"name":"Link","value":"<https://example.com/wp-json/>; rel=\"https://api.w.org/\""},{"name":"Vary","value":"Accept-Encoding"},{"name":"Content-Length","value":"168"},{"name":"Content-Type","value":"text/plain; charset=utf-8"}]}}
Actions

Also available in: Atom PDF