IDS/IPS placement in cloud networks: what traffic you can inspect
For developers and platform engineers deciding where network inspection belongs in a cloud architecture. This guide focuses on the practical constraint that matters most: encrypted east-west and north-south traffic is only inspectable where you can actually see plaintext or packet copies, and placement choices determine that.
TL;DR — In cloud networks, IDS/IPS visibility is determined less by the tool and more by where TLS terminates, whether traffic is mirrored or inline, and whether the provider even exposes packets on that path. The most likely correct design is: inspect north-south traffic at the edge/load balancer or reverse proxy, inspect east-west traffic with VPC/VNet mirroring plus host telemetry, and do not expect to see useful L7 payloads after end-to-end TLS unless you terminate or decrypt somewhere intentionally. Reading time: ~7 min
What it is and where it sits
For cloud networks, the real question is not "should we deploy IDS/IPS" but "on which hop can we still observe the bytes we care about?" Placement decides whether you get packet headers only, full HTTP requests, SQL queries, DNS payloads, or almost nothing beyond SNI, JA3-like fingerprints, and flow metadata.
In a typical app path, there are four common places to inspect:
- At the internet edge: CDN, WAF, load balancer, reverse proxy, ingress controller. Good for north-south HTTP(S), bad for seeing traffic that bypasses that edge.
- Inline in the VPC/VNet path: firewall appliance, gateway, service insertion. Good for blocking, but only sees what actually traverses it.
- Out-of-band via traffic mirroring: copy packets from ENIs/NICs/VMs to a sensor. Good for IDS, not IPS. Often misses managed-service internals and decrypted payloads.
- On the host or workload: eBPF, host firewall, process/network telemetry, sidecars. Good for seeing pre-encryption or post-decryption at the endpoint.
What it replaces depends on your current setup:
- If you already have a reverse proxy like nginx or Envoy terminating TLS, an edge IDS/IPS often replaces the idea of a separate network appliance for HTTP inspection.
- If you rely on security groups/NACLs/firewall rules only, IDS adds detection but does not replace policy enforcement.
- If your workloads talk directly over mTLS, packet IDS often cannot replace host-level telemetry because the network path never sees plaintext.
A typical request flow looks like this:
View as text
[Client] ---- 1. TLS/HTTP ----> [LB/Reverse Proxy] [Client] <---- 6. response ---- [LB/Reverse Proxy] [LB/Reverse Proxy] ---- 2. HTTP or re-TLS ----> [App] [App] ---- 3. SQL/TLS ----> [DB] [App] ---- 4. HTTPS ----> [External API] [Sensor via Mirror] <---- 5. packet copies ---- [App]
What each point can inspect:
- LB/Reverse Proxy: full HTTP method, path, headers, body if TLS terminates there.
- Mirror sensor on App NIC: packet headers and payload only if not encrypted on that hop.
- DB path: full SQL only if DB traffic is unencrypted or decrypted before capture; otherwise mostly metadata.
- External API egress: usually only destination IP/port, TLS handshake metadata, byte counts, timing, unless you run explicit TLS interception.
The cloud-specific constraint: many managed services do not expose packet capture points the way a physical switch SPAN port would. You may be able to mirror VM NICs, but not traffic entirely inside a managed load balancer, serverless platform, or provider-managed database fabric.
How it actually works
Take one realistic example: a public API behind nginx in Kubernetes or on VMs, with TLS terminated at nginx, app-to-DB over TLS, and outbound HTTPS to a payment provider.
Step-by-step path
- Client sends
POST /checkoutover HTTPS to the public endpoint. - nginx terminates TLS using its server certificate. At this exact point, plaintext HTTP exists in memory and logs. An IDS/IPS integrated here can inspect method, path, headers, body size, user agent, cookies, and request rate.
- nginx forwards to the app over HTTP on a private subnet, or re-encrypts with TLS. If it forwards plain HTTP, a mirrored sensor on that subnet can inspect the full request. If it re-encrypts, the mirrored sensor sees only TLS.
- App queries Postgres over TLS. A network IDS on the VPC path sees a TCP session to
5432and a TLS handshake, but not the SQL text. A host-level sensor on the app or DB host may still observe process-level connections or decrypted traffic depending on where it hooks. - App calls
https://api.payment.example. A network IDS sees destination IP, SNI if not encrypted by ECH, certificate metadata, timing, and byte counts. It does not see the JSON payload without explicit TLS interception. - Traffic mirroring copies packets from the app NIC to an IDS sensor. The sensor can alert on scanning, beaconing, suspicious DNS, odd egress patterns, and some protocol anomalies. It cannot block because mirrored traffic is out-of-band.
- Inline IPS at egress gateway can block known bad destinations or protocol violations, but only for traffic routed through it. If a subnet has a direct internet route, that traffic bypasses the IPS entirely.
What this means in practice
If your incident question is "did someone exploit /api/upload with a malicious multipart body?" then the place to inspect is the TLS terminator or the app itself. A mirrored sensor behind an end-to-end TLS path will not answer it.
If your incident question is "which workload started beaconing to a rare external host every 60 seconds?" then flow logs, mirrored packets, and egress gateway logs are enough even without payload decryption.
A quick sanity check on where TLS terminates:
openssl s_client -connect api.example.com:443 -servername api.example.com </dev/null 2>/dev/null | sed -n '/subject=/p;/issuer=/p;/ALPN/p'
Typical output shape:
subject=CN = api.example.com
issuer=C = US, O = Let's Encrypt, CN = R11
ALPN protocol: h2
That tells you the public edge certificate, not whether the next hop is plaintext. To verify what nginx sends upstream, inspect config directly:
nginx -T | sed -n '/server_name api.example.com/,/}/p'
Typical output shape:
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api/privkey.pem;
location / {
proxy_pass http://app_backend;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
If you see proxy_pass http://..., plaintext exists between nginx and the app. If you see proxy_pass https://..., a packet sensor on that segment will not see HTTP payloads.
You can also catch accidental bypasses. For example, if some clients hit the app node directly instead of the load balancer, your edge IPS sees nothing. A quick header probe often reveals this:
curl -k -I https://10.0.3.24/
Misconfigured direct-node output often looks like:
HTTP/1.1 301 Moved Permanently
Server: nginx/1.24.0
Date: Fri, 30 Oct 2026 10:12:44 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: https://api.example.com/
That redirect is useful, but it also proves the node is reachable directly. If your IPS is only at the load balancer, direct-node access bypasses it.
When to use it (and when not to)
Use placement to answer a concrete inspection question, not because "we need IDS/IPS" in the abstract.
| Scenario | Recommendation |
|---|---|
| Public HTTP API, TLS terminates at reverse proxy or ingress | Put HTTP inspection at that proxy/ingress. Add mirrored IDS for lateral movement and egress anomalies. |
| East-west traffic is mostly mTLS between services | Use host/workload telemetry and service-mesh or app-layer logs. Packet IDS alone will have poor payload visibility. |
| Need active blocking for egress to known bad destinations | Put IPS inline at the egress gateway/firewall and force routes through it. |
| Need forensic visibility without adding latency | Use traffic mirroring plus flow logs. Accept that it detects but does not block. |
| Mostly managed/serverless services with little packet access | Lean on provider logs, app logs, DNS logs, and endpoint/workload telemetry. Network IDS may be shallow. |
| Small internal app on private subnets, strict SG rules, no compliance driver | You probably don't need a full IPS. Start with flow logs, DNS logging, and host-based detection. |
You probably do not need a dedicated inline IPS if:
- Your main concern is application abuse on HTTPS endpoints and you already terminate TLS at nginx/Envoy; inspect there instead.
- Your environment is mostly managed services where you cannot steer all traffic through an appliance.
- Your team cannot operate signature tuning, false-positive review, and emergency bypass procedures.
Trade-offs
Every placement gives visibility somewhere and blindness somewhere else.
-
Edge inspection benefit: sees full HTTP after TLS termination. Cost: only covers traffic that actually hits that edge; internal east-west and direct-node paths can bypass it.
-
Inline IPS benefit: can block in real time. Cost: adds latency, creates a failure domain, and requires route control. Misconfigurations cause outages, not just missed alerts.
-
Traffic mirroring benefit: no inline latency, easier rollout, good for forensics. Cost: cannot block, can be expensive at volume, and often misses plaintext because mirrored packets are still encrypted.
-
Host/workload telemetry benefit: can observe pre-encryption/post-decryption context and process identity. Cost: agent overhead, kernel/runtime compatibility work, and more moving parts per node.
-
TLS interception benefit: restores L7 visibility for outbound HTTPS. Cost: certificate management, trust distribution, app breakage with pinning/mTLS, privacy/compliance implications, and developer hostility for good reasons.
-
Centralized appliance benefit: one place to manage policy. Cost: cloud lock-in via provider-specific routing/mirroring constructs and uneven support across VM, container, and serverless paths.
In practice
Example 1: Confirm whether nginx creates an inspectable plaintext hop
upstream app_backend {
server 10.0.2.15:8080;
server 10.0.2.16:8080;
}
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api/privkey.pem;
location / {
proxy_pass http://app_backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
This terminates TLS at nginx and forwards plaintext HTTP to the app pool, which means a sensor on that private segment can inspect full requests. Gotcha: if you later change proxy_pass to https://app_backend during a hardening pass, your mirrored IDS loses HTTP visibility immediately unless you move inspection to nginx or the app.
Example 2: Capture on the app host to prove what the network sensor can and cannot see
⚠️ Packet capture on production hosts can expose credentials, session cookies, and personal data. Write captures to encrypted storage, restrict file permissions, and delete them after analysis.
sudo tcpdump -i eth0 -nn -s0 -A 'host 10.0.1.10 and port 8080'
If nginx forwards plaintext HTTP to the app on 10.0.1.10:8080, you will see request lines and headers:
POST /checkout HTTP/1.1
Host: api.example.com
X-Forwarded-For: 203.0.113.44
Content-Type: application/json
Content-Length: 87
Now compare with the DB path:
sudo tcpdump -i eth0 -nn -s0 -A 'host 10.0.5.20 and port 5432'
Typical output shape for TLS-protected Postgres is not SQL text, just TLS records:
IP 10.0.2.15.51544 > 10.0.5.20.5432: Flags [P.], length 296
...."....
....TLSv1.3....
What it does: proves whether your IDS placement can ever see useful payloads on that hop. Gotcha: -A prints ASCII and can be misleading on binary protocols; use it only for a quick check, not long captures.
Example 3: Force all egress through an inline inspection point on Linux
ip route
sudo ip route replace default via 10.0.9.254 dev eth0
ip route get 1.1.1.1
Typical verification output:
1.1.1.1 via 10.0.9.254 dev eth0 src 10.0.2.15 uid 1000
cache
This changes the default route so outbound traffic traverses the gateway where an inline IPS could sit. Gotcha: if that gateway is wrong or unhealthy, you just black-holed outbound traffic.
Further reading
- NIST SP 800-41 Rev. 1, Guidelines on Firewalls and Firewall Policy
- The
ngx_http_proxy_modulesection of the nginx documentation tcpdumpman page- The "HTTP access control (CORS)" and "HTTP headers" sections of the MDN HTTP docs
- Suricata User Guide, especially AF_PACKET, NFQUEUE, and Eve JSON output
This article was written by an AI system and published pending human review. Verify anything you intend to act on.
Have a project in mind?
Get an instant AI price estimate for it, or talk directly to our team.
One email a month on what we learn building with AI