Zero-Trust Webhook Security: HMAC, mTLS, and What Actually Ships in 2026
Zero-Trust Webhook Security: HMAC, mTLS, and What Actually Ships in 2026 Webhooks are one of the few pieces of internet plumbing that break the normal trust model of the web.

Zero-Trust Webhook Security: HMAC, mTLS, and What Actually Ships in 2026
Webhooks are one of the few pieces of internet plumbing that break the normal trust model of the web. Every other inbound request to your server arrives because you initiated a session — a user logged in, a client called your API. A webhook is different: it's an unsolicited POST from someone else's infrastructure, hitting a public URL, and your server has to decide in milliseconds whether to trust it.
For most of the last decade, the industry answered that question with HMAC signatures. In 2026, a second answer — mutual TLS (mTLS) — is getting a lot louder, especially in security and Zero-Trust circles. This post lays out how each approach actually works, where mTLS webhook security is genuinely gaining ground, and where the "HMAC is dead" framing you'll see in a lot of vendor content doesn't hold up against how the biggest webhook senders actually operate today.
The Trust Problem, in Plain Terms
Your webhook endpoint is a public URL. Anyone who discovers it — through logs, browser history, a leaked config, or simple guessing — can POST to it. Without verification, an attacker can forge a payment.succeeded event, fake an order confirmation, or inject arbitrary data into your system. Verification is what separates "a request arrived" from "a request I can act on."
HMAC Signatures: Still the Default, Not a Relic
Despite the "legacy" framing you'll see in some 2026 marketing copy, HMAC-SHA256 signing remains the dominant verification method for the webhook providers that actually move the most traffic. Stripe, GitHub, Shopify, Slack, and Twilio all sign payloads with a shared secret and a header the receiver recomputes and compares — GitHub uses X-Hub-Signature-256, Shopify uses X-Shopify-Hmac-Sha256, Stripe composes a timestamp-plus-body string into its Stripe-Signature header, and Slack does something structurally similar with v0=. A few providers (Discord, SendGrid) use asymmetric public-key signatures instead of a shared secret, which sidesteps the "secret sprawl" problem while staying entirely at the application layer.
There's also been real consolidation here: Svix stewards Standard Webhooks, an open specification that a growing number of platforms have adopted so that HMAC verification code, timestamp tolerance, and replay protection work the same way across providers instead of every vendor reinventing it slightly differently.
Where HMAC genuinely falls short
The classic critique of HMAC is fair, and worth restating precisely:
- It's an application-layer check. Your server has to accept the TCP connection, complete the TLS handshake, parse HTTP headers, and read the body into memory before it can verify anything. An oversized or malformed payload can consume resources before your signature check ever runs.
- Shared secrets don't scale cleanly. Because HMAC is symmetric, both sides hold the same secret. In sprawling microservice setups, secrets end up in environment variables, config files, and occasionally version control. A leak on either end compromises the channel.
- Compute adds up. Hashing large payloads at high volume is real (if usually modest) CPU overhead.
- Sloppy implementations leak timing information. Comparing signatures with
==instead of a constant-time comparison function can, in theory, let an attacker infer bytes of a valid signature. This is a well-known and avoidable implementation bug, not an inherent flaw in HMAC as a scheme.
None of this means HMAC is broken — it means HMAC verification has to happen correctly, and it happens after your infrastructure has already accepted the connection.
Enter mTLS: Authentication Before the Request Exists
Standard TLS (the "S" in HTTPS) only proves the server's identity to the client. Mutual TLS flips that around: both sides present X.509 certificates during the handshake, and the connection is refused if the client certificate isn't valid, trusted, and unexpired. Critically, this happens before any HTTP request is parsed — an unauthenticated sender can't get an HTTP response, malicious or otherwise, because there's no TLS session to send one over.
How a TLS 1.3 mutual handshake actually works
- Client Hello — the sender proposes cipher suites and a random value.
- Server Hello + Server Certificate — the receiver responds and presents its own certificate.
- Certificate Request — the receiver demands the client identify itself, listing which Certificate Authorities it trusts.
- Client Certificate + Certificate Verify — the sender presents its certificate and signs the handshake transcript with its private key.
- Server Verification — the receiver checks the signature, the certificate chain, expiry, and revocation status.
- Secure Tunnel Established — only now do the two sides exchange session keys and start sending the actual HTTP payload.
If step 4 fails, the connection drops. The application never sees the request.
Is mTLS Actually Replacing HMAC for Webhooks in 2026? A Reality Check
This is the part where a lot of the current content around "mTLS webhooks" overstates things, so it's worth being precise about what's actually happening in the market:
mTLS adoption is real and growing — but it's concentrated in specific places, not replacing HMAC for general-purpose SaaS webhooks. The clearest 2026 pattern is:
- Internal service-to-service traffic and Zero-Trust network access. mTLS is the default identity mechanism inside service meshes like Istio/Envoy, and workload-identity systems like SPIFFE/SPIRE exist specifically to automate certificate issuance for this use case. This is the fastest-growing area for mTLS by far.
- Regulated financial data exchange. The clearest real-world example of mandated certificate-based mutual authentication for external, webhook-like traffic is European open banking. Under PSD2's technical standards, third-party providers must present a Qualified Website Authentication Certificate (QWAC) — issued by an eIDAS-regulated trust service provider — to authenticate to a bank's API. This carries forward into the incoming PSD3 and Payment Services Regulation (PSR) framework: the EU Parliament, Council, and Commission agreed final text in April 2026, with formal application expected around 2027. It's worth noting implementation isn't perfectly uniform — some banks (Nordea is a documented example) currently rely on standard TLS plus certificate-based signing (QSealC) rather than full mTLS on the connection itself, so "PSD2/PSD3 mandates mTLS everywhere" is a simplification of a more mixed reality.
- Public SaaS-to-SaaS webhooks are still overwhelmingly HMAC. Stripe, GitHub, Shopify, Twilio, and the rest send webhooks to thousands of independent, unaffiliated receivers who each run their own infrastructure. Requiring every one of those receivers to run a certificate-verifying edge proxy would be a significant adoption barrier — which is exactly the operational complexity problem described below. As of 2026, none of the major webhook senders have replaced signature-based verification with mTLS for their general webhook products.
So the accurate framing isn't "mTLS is replacing HMAC" — it's "mTLS is becoming the default for internal and tightly-coupled B2B traffic, while HMAC (increasingly standardized via specs like Standard Webhooks) remains the pragmatic default for public, many-to-many webhook delivery."
What regulation actually requires (and doesn't)
- PCI DSS v4.0 requires strong cryptography (TLS 1.2+) for any transmission of cardholder data over public networks, and pushes API implementations toward strong client authentication generally. It does not mandate mTLS specifically as a blanket requirement — mTLS and certificate-based client authentication are cited as acceptable strong-authentication options, alongside mechanisms like OAuth 2.0.
- NIST SP 800-207 (Zero Trust Architecture) and the associated US federal Zero Trust strategy are the actual government-side drivers behind the "verify before you trust the network" posture referenced by GovTech vendors — mTLS is one of the concrete mechanisms organizations use to implement that principle, not a named line-item requirement of a single regulation.
- HIPAA requires encryption of PHI in transit but, like PCI DSS, doesn't prescribe mTLS by name.
Why mTLS Isn't the Default Everywhere: The Real Operational Cost
The reason mTLS hasn't displaced HMAC for general webhook delivery isn't security — it's Public Key Infrastructure (PKI) operations:
- Running a private CA. You can't use a public CA like Let's Encrypt for internal client-identity certificates; you need your own root and intermediate CAs, secured appropriately.
- Certificate lifecycle management. Certificates expire. Automated issuance, distribution, and rotation before expiry is required to avoid outages — this is precisely the problem tools like cert-manager and its companion trust-manager (both CNCF-adjacent, used heavily in Kubernetes) exist to solve, and it's also the core use case for SPIFFE/SPIRE and commercial options like Smallstep's step-ca.
- Revocation infrastructure. You need highly available OCSP responders or CRL publishing so edge proxies can reject a compromised certificate immediately.
- Correct edge configuration. Getting
ssl_verify_client, SAN extraction, and CA trust chains right across NGINX, Envoy, HAProxy, or a cloud API gateway is easy to get subtly wrong — and a misconfiguration can silently fail open, accepting unauthenticated traffic instead of rejecting it.
An illustrative (not copy-paste-production-ready) NGINX mTLS block looks like this:
server {
listen 443 ssl;
server_name webhooks.enterprise.com;
# Standard server-side TLS
ssl_certificate /etc/ssl/certs/server_cert.pem;
ssl_certificate_key /etc/ssl/private/server_key.pem;
# mTLS client verification
ssl_client_certificate /etc/ssl/certs/trusted_client_ca.pem;
ssl_crl /etc/ssl/certs/crl.pem;
ssl_verify_client on;
ssl_verify_depth 2;
location /webhook-receive {
proxy_set_header X-Client-Verified $ssl_client_verify;
proxy_set_header X-Client-DN $ssl_client_s_dn;
if ($ssl_client_verify != SUCCESS) {
return 403 "Client certificate verification failed";
}
proxy_pass http://internal_webhook_processor;
}
}
Cloud platforms have lowered this bar somewhat — AWS API Gateway, Cloudflare (via API Shield / Access), and most modern service meshes support mTLS termination without you having to hand-roll the NGINX config above — but you still own certificate issuance, rotation, and revocation policy.
A Practical Recommendation
Given how this actually shakes out in 2026:
- If you're sending or receiving public, many-to-many webhooks (SaaS-to-SaaS integrations, third-party developers, unknown receivers), HMAC — ideally implemented against the Standard Webhooks spec, with timestamp-based replay protection, constant-time comparison, and supported secret rotation — remains the pragmatic, low-friction default. It's also what your integration partners already expect.
- If you're authenticating internal services or a fixed set of known, high-trust partners (internal microservices, a payments processor you have a direct contractual relationship with, a regulated open-banking channel), mTLS is the stronger control, particularly if you're already running the PKI tooling (cert-manager, SPIFFE/SPIRE, a service mesh) to support it elsewhere in your stack.
- A hybrid model is common and reasonable: enforce mTLS at the edge for your trusted internal and B2B traffic, while continuing to accept HMAC-signed webhooks from external SaaS providers you don't control the infrastructure of.
The two approaches solve overlapping but not identical problems. HMAC proves the payload wasn't tampered with and came from someone holding the secret; mTLS proves the connection itself is coming from a cryptographically trusted identity, before your application ever sees a byte. Treat the choice as an architectural decision based on who's on the other end of the connection — not a wholesale migration from one "legacy" method to one "correct" one.
Further Reading
- Cloudflare — What is mTLS?
- Apache APISIX — What is Mutual TLS?
- Red Hat Developer — Implement mTLS and Zero Trust with cert-manager and trust-manager
- Hookdeck — How to Implement SHA-256 Webhook Signature Verification
- Svix — Standard Webhooks specification
- Norton Rose Fulbright — PSD3 and PSR: From Provisional Agreement to 2026 Readiness
- TrueLayer Help Centre — Do I need an eIDAS certificate?