What UTM Actually Costs You: SSL/TLS Deep Inspection Internals on FortiGate

Two modes, one checkbox, very different machines

Part 4 of the NSE4 series introduced the two SSL inspection profiles a FortiGate can run: certificate inspection and deep inspection. That post covered what each one is for. This one covers what’s actually happening on the box when you flip between them, because the two modes aren’t a strength dial on the same feature. They’re architecturally different things wearing the same profile name, and the difference is the entire reason one of them is nearly free and the other one shows up as a line item on your sizing spreadsheet.

Certificate inspection reads the SNI (Server Name Indication) field from the TLS ClientHello and the certificate presented in the ServerHello, and does nothing else to the session. The FortiGate learns the hostname, learns the certificate’s subject and issuer, makes a policy decision based on that, and lets the encrypted bytes pass through untouched in both directions. No decryption happens. No new certificate gets minted. The client’s TLS session terminates exactly where it always would: at the real server.

Deep inspection does something categorically different. The FortiGate becomes a real man-in-the-middle, deliberately and by design. It terminates the client’s TLS session itself, opens its own separate TLS session to the real server, and sits in between re-encrypting traffic in both directions. To make that work without every browser on the network throwing a certificate warning, the FortiGate re-signs the real server’s certificate on the fly, using its own CA, and presents that re-signed certificate to the client. The client’s browser is talking to a certificate FortiOS just generated, not the one the server actually holds.

That’s the fork in the road. Everything else in this post is downstream of it.

What certificate inspection buys you, and where it stops

Certificate inspection is cheap because it’s not really inspecting the session, it’s inspecting the handshake metadata around the session. The FortiGate never needs the session key, never decrypts a single application-layer byte, and the flow can still be a candidate for NP7 hardware offload once policy has been evaluated. Part 1 of the packet-flow series covers what NP7 offload actually means for a session; certificate inspection is compatible with staying on that fast path in a way deep inspection structurally cannot be.

What you get from it: category-based web filtering by SNI, application identification for anything that announces itself cleanly in the handshake, and blocking or allowing by certificate subject/issuer. What you don’t get: any visibility into what’s actually inside the encrypted payload. Antivirus can’t scan a file it never decrypts. Application control can’t distinguish “this HTTPS session is normal browsing” from “this HTTPS session is a file upload to a blocked category” once both are wrapped in the same encrypted tunnel to the same SNI. Part 6 of the NSE4 series covers this gap directly: cloud-app filtering that needs to distinguish activity within an app, rather than just identifying the app, depends on deep inspection. Certificate inspection gets you the door number. Deep inspection is the only mode that lets you see what’s happening in the room.

What deep inspection actually costs

The re-signing step is the tell. Every certificate the FortiGate mints has to be generated, signed, and cached, and every byte of every session in deep inspection has to be decrypted, inspected, and re-encrypted before it goes anywhere. That work does not run on the NP7. Part 1 of the packet-flow series explains why: NP7 offload exists for sessions the ASIC can forward without touching payload content. A session under deep inspection has its payload actively rewritten mid-flow. That’s not a fast-path candidate by definition, it’s the kind of work the fast path exists to avoid.

Some of the cryptographic load lands on the CP9 content processor rather than the general-purpose CPU where platforms have one, which is why “deep inspection” and “CPU meltdown” aren’t strictly synonymous the way they were on older hardware. But CP9 offload covers the crypto operations, not the inspection logic sitting on top of them, and a platform sized against its NP7-offloaded firewall throughput number will not hit anything close to that number once a meaningful fraction of sessions are running full deep inspection. This is the same lesson the traffic shaping post makes about shaping and UTM stacking: configurations that force sessions off the NP7 fast path work correctly, they just work at a throughput cost that’s easy to miss in a lab and expensive to discover in production. Deep inspection is the single biggest contributor to that gap. Size for it explicitly, don’t assume the brochure throughput number survives contact with a policy that deep-inspects everything.

TLS 1.3 removed the easy middle ground

Older writeups on SSL inspection describe a third option that doesn’t really exist anymore: peek at the handshake, and only escalate to full decryption for sessions that look interesting. TLS 1.3 broke that model at the protocol level, and it’s worth understanding why, because it explains why the certificate-inspection/deep-inspection split got sharper rather than softer as TLS versions moved forward.

In TLS 1.2 and earlier, the server’s certificate travels in the ServerHello in plaintext, visible to anything watching the handshake. A middlebox could read it, decide the traffic was uninteresting, and just let the session establish normally without ever being a party to it. TLS 1.3 encrypts the server’s certificate as part of the handshake, using key material derived earlier in the exchange. A passive observer, which is what certificate inspection fundamentally is, can no longer read the server’s certificate at all. What TLS 1.3 leaves visible is the SNI in the ClientHello (still sent in plaintext unless Encrypted Client Hello is in play, which as of this writing is not yet the default posture most sites deploy) and the certificate the FortiGate itself presents if it’s running deep inspection.

The practical consequence: certificate inspection under TLS 1.3 is really SNI inspection with a name that undersells what it’s now missing. It still does real, useful work for hostname-based policy, but the “inspect the server’s actual certificate” half of its job quietly stopped being possible for TLS 1.3 sessions specifically. If your web filtering policy was ever built on server-certificate subject/issuer matching rather than SNI, TLS 1.3 adoption on the sites you’re filtering is the thing that silently degraded it, not a Fortinet configuration change.

Encrypted Client Hello (ECH), where it’s deployed, closes the remaining gap by encrypting the SNI too. That’s not yet the common case, but it’s the direction the protocol is heading, and it’s worth knowing that certificate inspection’s entire value proposition depends on a piece of plaintext that the IETF has already standardized a mechanism to remove.

The trust problem deep inspection creates

Every certificate the FortiGate re-signs during deep inspection has to chain back to a CA the client actually trusts, or every single HTTPS site turns into a certificate warning. That means distributing the FortiGate’s inspection CA certificate to every managed endpoint’s trust store, which is a fleet-management problem wearing a firewall-feature costume. The PKI series covers the CA architecture question for IPsec identity, but the same “who is your CA, and how does trust get onto every device without a human in the loop forever” question applies here, just pointed at a different problem: instead of devices trusting each other over IPsec, it’s endpoints trusting a certificate the firewall generates on demand.

Get the distribution right, typically via the same MDM or GPO channel that already pushes other managed-device policy, and deep inspection is invisible to users on managed devices. Get it wrong, or leave it to users to click through warnings, and you’ve trained your entire user population to ignore certificate errors, which is a security regression dressed up as a security feature.

Where pinning breaks the model entirely

Certificate pinning is an application deliberately refusing to trust anything except one specific certificate or CA, regardless of what the operating system trust store says. That’s the app developer’s explicit defense against exactly the kind of interception deep inspection performs. A pinned app under deep inspection doesn’t throw a certificate warning a user can click through. It fails outright, usually with an error message that gives no indication the firewall is the cause.

Banking apps, some enterprise SaaS clients, and a fair number of mobile apps pin. The fix isn’t a FortiOS setting that makes pinning stop mattering, because there isn’t one. It’s an explicit exemption, by hostname or category, that routes those flows through certificate inspection or straight passthrough instead of deep inspection. That means someone has to know which apps in the environment pin, which is usually discovered the hard way, once, per app, when it breaks. Building the exemption list before deployment rather than reactively after the help desk tickets start is the entire difference between “deep inspection rollout” and “deep inspection incident.”

Where this leaves the SSL profile decision

Neither mode is the correct default in the abstract. Certificate inspection is the right call for anything where content visibility doesn’t matter and you want SNI-based policy at effectively no cost: general category filtering on a link that isn’t sized for CPU-bound inspection, or any segment where pinned-app breakage is unacceptable and the visibility trade isn’t worth it. Deep inspection is the right call anywhere antivirus, full application control, or IPS pattern matching on HTTPS actually needs to see payload, sized with the explicit understanding that those sessions leave the NP7 fast path and cost CPU and CP9 cycles accordingly, with a trust-distribution plan and a pinning exemption list built before rollout rather than during the incident that follows skipping that step.

The question worth asking before either profile goes on a policy isn’t “should we inspect HTTPS.” It’s “does this specific traffic need payload visibility badly enough to pay the CPU cost and the pinning-breakage risk that comes with actually getting it,” and for a meaningful share of traffic on most networks, the honest answer is no.