Skip to content

mTLS Best Practices

Use mutual TLS on Azure App Service as a deliberate trust-boundary control, not as a single feature toggle. Inbound client certificate authentication, outbound certificate presentation, and certificate lifecycle management each need separate validation, ownership, and rollback plans.

Why This Matters

mTLS helps App Service workloads answer two different questions:

  • Who is calling my app?
  • What certificate does my app present when it calls something else?
flowchart TD
    Caller[Client with certificate] --> Edge[App Service front end]
    Edge --> Header[X-ARR-ClientCert]
    Header --> App[App code validates chain and policy]
    App --> Vault[Certificate source and rotation]
    App --> Downstream[Remote service with outbound mTLS]

If you blur those concerns together, teams often assume the platform validated trust when it only forwarded a certificate, or they assume an uploaded certificate is automatically used for outbound calls when the application never loaded it.

Terminate inbound client certificates at the platform

Use App Service client certificate handling instead of trying to terminate inbound TLS inside framework code.

Why:

  • App Service already terminates TLS at the front end.
  • Your app receives a stable X-ARR-ClientCert header contract.
  • Operational controls stay in site configuration rather than custom reverse-proxy code.

Portal view: Incoming client certificates configuration

Configuration General settings blade for a Web App scrolled down to the Incoming client certificates section, with General settings (active), Stack settings, Health check, Path mappings, and Error pages tabs visible at the top and a Refresh command bar action beneath the tabs. Above the section the remaining transport controls are visible: Session affinity proxy (unchecked), HTTPS only (unchecked), Minimum Inbound TLS Version (1.2), SCM Minimum Inbound TLS Version (1.2), Minimum Inbound TLS Cipher Suite (TLS_RSA_WITH_AES_128_CBC_SHA, Default) with a Change link, and End-to-end TLS encryption (unchecked). A Debugging section shows Remote debugging (unchecked). The Incoming client certificates section presents Client certificate mode as four radio options — Required (with description "All requests must be authenticated through a client certificate."), Optional (with description "Clients will be prompted for a certificate, if no certificate is provided fallback to SSO or other means of authentication. Unauthenticated requests will be blocked."), Optional Interactive User (with description "Clients will not be prompted for a certificate by default. Unless the request can be authenticated through other means (like SSO), it will be blocked."), and Ignore (selected, with description "No client authentication is required. Unauthenticated requests will not be blocked."). Apply and Discard buttons are at the bottom.

The Incoming client certificates section is the single Portal surface where the platform-side half of mTLS is configured. The visible Ignore default is the App Service factory state and the most dangerous starting point for any app that intends to use client certificate authentication — the platform will simply not request a certificate, so application code that checks X-ARR-ClientCert will never see one. The four Client certificate mode options map directly to the trust models discussed throughout this guide: Required for true mTLS endpoints where every caller must present a certificate, Optional to prompt but fall back to other auth, Optional Interactive User for browser scenarios where prompting would hurt UX, and Ignore only for routes that have no mTLS expectation. Note also that HTTPS only is unchecked in this capture — mTLS without HTTPS only is meaningless, because the platform contract that makes X-ARR-ClientCert trustworthy depends on traffic terminating at the App Service front end over TLS.

Validate the forwarded certificate in app code

Microsoft Learn is explicit: App Service forwards the client certificate but does not validate it.

Validate at least:

  • thumbprint or public key pinning policy where appropriate
  • issuer or issuing CA
  • subject or SAN
  • validity window
  • chain trust and revocation policy if required by your security model

Keep excluded routes narrow and intentional

Use clientCertExclusionPaths only for routes that truly cannot participate in mTLS, such as:

  • /health
  • platform or synthetic probe endpoints
  • third-party webhook receivers with no client-certificate support

Document every exclusion with the business reason and compensating control.

Exclusions can introduce protocol-level side effects

Microsoft Learn documents that clientCertExclusionPaths and OptionalInteractiveUser depend on TLS renegotiation. Because TLS 1.3 and HTTP/2 do not support renegotiation, and large request bodies can fail in renegotiated paths, use exclusions only when you understand those transport limits.

Separate inbound and outbound mTLS ownership

Treat these as separate controls:

  • Inbound mTLS: caller authentication and route policy
  • Outbound mTLS: application credential material, store access, TLS client configuration, and certificate rotation

This separation makes incident response clearer and avoids mixing partner-caller trust issues with downstream service-auth failures.

Store outbound certificate material outside source code

Use App Service certificate-loading patterns and configuration indirection instead of embedding certificate files in the repository.

Recommended direction:

  • keep certificate material in managed certificate storage or Key Vault-backed workflows
  • use App Service configuration to expose only what the runtime needs
  • rotate by reference and thumbprint-driven rollout, not by editing application code

Note

Key Vault references are a strong pattern for configuration indirection in App Service, but confirm your exact certificate-loading workflow for private certificates and outbound mTLS before standardizing rotation steps.

Common Mistakes / Anti-Patterns

Assuming clientCertMode=Required validates the chain

It does not. Required means the front end expects a client certificate. Your application still owns trust validation.

Trusting X-ARR-ClientCert from untrusted paths

Only trust the header when traffic is known to have passed through App Service front ends and HTTPS-only is enforced.

Using Optional mode and assuming authentication happened

Optional allows requests without a client certificate. If your app needs certificate-based auth, enforce presence in code or use Required for that route surface.

Forgetting outbound certificate loading configuration

Uploading a certificate is not enough. The application must be configured to load it and attach it to outbound TLS clients.

Treating /health as a reason to weaken the whole app

Use targeted exclusions instead of downgrading the entire site to Optional just to satisfy health probes.

Validation Checklist

  • httpsOnly is enabled for the web app.
  • clientCertEnabled is explicitly configured.
  • clientCertMode matches the intended trust model.
  • clientCertExclusionPaths contains only justified routes.
  • Application code parses X-ARR-ClientCert and validates the certificate.
  • Outbound certificate loading is documented separately from inbound auth.
  • Rotation and expiration checks exist for outbound certificates.

See Also

Sources