mTLS Failures¶
This playbook covers mutual TLS (mTLS) failures on Azure App Service, ranging from client certificate configuration issues to app-side chain validation and certificate loading problems.
1. Summary¶
Symptoms¶
X-ARR-ClientCertis missing in app code- App Service returns
403when inbound client certificates are required - Application code fails certificate parsing or trust validation
- Outbound mTLS calls fail because the client certificate cannot be found or loaded
- ASE deployments show behavior differences between expected private ingress and actual certificate flow
Common error messages¶
X-ARR-ClientCertheader absent from the request403from the App Service front end- Chain validation failure in application code
clientCertEnabledis false in site configuration
flowchart TD
Start[mTLS failure] --> A{Inbound or outbound?}
A -->|Inbound header missing| B[Check client certificate site settings]
A -->|Front-end 403| C[Check caller certificate presentation]
A -->|App validation failure| D[Check parsing and trust policy]
A -->|Outbound certificate missing| E[Check certificate loading configuration]
A -->|ASE topology confusion| F[Check ingress path and ASE design] 2. Common Misreadings¶
| Observation | Often Misread As | Actually Means |
|---|---|---|
X-ARR-ClientCert missing | Platform bug | clientCertEnabled=false or clientCertExclusionPaths matches the request path |
403 from front end | App-side auth failure | Caller never presented a certificate; platform rejected the handshake |
App parses X-ARR-ClientCert and fails | Certificate is corrupt | App is treating base64-encoded content as full PEM without adding markers |
| Outbound certificate not found | Certificate did not upload | WEBSITE_LOAD_CERTIFICATES thumbprint mismatch, OS-specific store path wrong, or app not restarted after config change |
| Behavior differs on ASE | ASE bug | Ingress topology is different from public multitenant path; upstream gateway may alter certificate flow |
3. Competing Hypotheses¶
| Hypothesis | Key Discriminator |
|---|---|
H1: clientCertEnabled false or exclusion path match | az webapp show --query clientCertEnabled returns false, or request path matches an entry in clientCertExclusionPaths |
| H2: Caller not presenting certificate | Request without --cert returns 403; request with --cert reaches the app |
| H3: App-side chain/parse failure | Certificate is present in the header but parsing or validation throws in application code |
| H4: Outbound cert not loaded correctly | WEBSITE_LOAD_CERTIFICATES set but application-side lookup returns null |
| H5: ASE ingress topology confusion | Behavior differs between multitenant test app and ASE-hosted app despite identical configuration |
4. What to Check First¶
-
Confirm
clientCertEnabledis true andclientCertModematches the intended behavior:az webapp show \ --resource-group $RG \ --name $APP_NAME \ --query "{clientCertEnabled:clientCertEnabled,clientCertMode:clientCertMode,clientCertExclusionPaths:clientCertExclusionPaths,httpsOnly:httpsOnly}" \ --output jsonCommand Purpose az webapp show --resource-group $RG --name $APP_NAME --query "{clientCertEnabled:clientCertEnabled,clientCertMode:clientCertMode,clientCertExclusionPaths:clientCertExclusionPaths,httpsOnly:httpsOnly}" --output jsonShows the client-certificate and HTTPS settings so you can verify how App Service handles inbound mTLS for this app. --resource-group $RGSelects the resource group that contains the target web app or related resource. --name $APP_NAMESpecifies the target web app name for this command. --query "{clientCertEnabled:clientCertEnabled,clientCertMode:clientCertMode,clientCertExclusionPaths:clientCertExclusionPaths,httpsOnly:httpsOnly}"Projects only these named top-level properties into a smaller object before formatting the output. --output jsonFormats the command output as JSON for full-fidelity inspection. -
Verify the request path is not covered by
clientCertExclusionPaths. -
Confirm the expected certificate thumbprint matches and the application has restarted after configuration change.
-
For ASE deployments, verify where ingress actually enters the App Service front-end layer.
5. Evidence to Collect¶
5.1 CLI Investigation¶
az webapp show \
--resource-group $RG \
--name $APP_NAME \
--query "{clientCertEnabled:clientCertEnabled,clientCertMode:clientCertMode,clientCertExclusionPaths:clientCertExclusionPaths,httpsOnly:httpsOnly}" \
--output json
| Command | Purpose |
|---|---|
az webapp show --resource-group $RG --name $APP_NAME --query "{clientCertEnabled:clientCertEnabled,clientCertMode:clientCertMode,clientCertExclusionPaths:clientCertExclusionPaths,httpsOnly:httpsOnly}" --output json | Shows the client-certificate and HTTPS settings so you can verify how App Service handles inbound mTLS for this app. |
--resource-group $RG | Selects the resource group that contains the target web app or related resource. |
--name $APP_NAME | Specifies the target web app name for this command. |
--query "{clientCertEnabled:clientCertEnabled,clientCertMode:clientCertMode,clientCertExclusionPaths:clientCertExclusionPaths,httpsOnly:httpsOnly}" | Projects only these named top-level properties into a smaller object before formatting the output. |
--output json | Formats the command output as JSON for full-fidelity inspection. |
curl --include "https://$APP_NAME.azurewebsites.net/cert-info"
curl --include \
--cert ./client.pem \
--key ./client.key \
"https://$APP_NAME.azurewebsites.net/cert-info"
5.2 What to Look For¶
clientCertEnabled=false: missing header is expected- Request path matches
clientCertExclusionPaths: missing header can be expected on that route httpsOnly=false: callers may be bypassing the intended TLS path- If only the second request (with
--cert) reaches the app, the platform enforcement path is working and the failing caller is not presenting a certificate - Treating
X-ARR-ClientCertas full PEM instead of base64 content - Failing to add PEM markers before parsing
- Validating only CN when the security model depends on issuer or SAN
- Expired intermediate or untrusted issuing CA in your application trust policy
6. Validation and Disproof by Hypothesis¶
H1: X-ARR-ClientCert header missing¶
Proves if clientCertEnabled is false, or the request path matches a clientCertExclusionPaths entry, or httpsOnly is false and callers are using HTTP.
Disproves if clientCertEnabled is true, request path is not excluded, and HTTPS is enforced.
Validation steps:
- Confirm
clientCertEnabledis true. - Confirm the request path is not covered by
clientCertExclusionPaths. - Confirm
httpsOnlyis true or callers are verified to be using HTTPS.
H2: 403 from front end with certificate required¶
Proves if a request without --cert returns 403 while a request with --cert reaches the app.
Disproves if both requests (with and without certificate) fail identically, suggesting a different cause.
Validation steps:
- Compare a request without and with a certificate using
curl. - If only the second request reaches the app, the failing caller is not presenting a certificate.
H3: Chain validation failure in app code¶
Proves if the certificate is present in the X-ARR-ClientCert header but parsing or validation throws in application code.
Disproves if the header is absent (which points to H1 or H2 instead).
Validation steps:
- Check for treating
X-ARR-ClientCertas full PEM instead of base64 content. - Check for failing to add PEM markers before parsing.
- Check for validating only CN when the security model depends on issuer or SAN.
- Check for expired intermediate or untrusted issuing CA in the application trust policy.
H4: WEBSITE_LOAD_CERTIFICATES certificate missing¶
Proves if WEBSITE_LOAD_CERTIFICATES is set but the application-side lookup returns null or cannot find the certificate.
Disproves if the certificate loads correctly and the issue is in a different stage of the mTLS flow.
Validation steps:
- Verify thumbprint matches exactly.
- Verify expected certificate format was uploaded.
- Verify the application is looking in the correct OS-specific location or store.
- Verify the app restarted after configuration change.
Certificate-loading details are OS-specific
On App Service, outbound certificate access differs between Windows and Linux. Validate your exact hosting OS before debugging application code.
H5: ASE-specific ingress topology¶
Proves if behavior differs between a multitenant test app and an ASE-hosted app despite identical configuration.
Disproves if the same behavior manifests on both multitenant and ASE environments.
Validation steps:
- Confirm where ingress actually enters the App Service front-end layer.
- Confirm whether an upstream proxy or gateway changes the expected request path.
- Confirm whether the ingress chain preserves the standard
X-ARR-ClientCertapplication contract.
7. Likely Root Cause Patterns¶
| Pattern | Evidence | Resolution |
|---|---|---|
| Client cert not enabled | clientCertEnabled=false | Enable client certificate authentication and choose the intended clientCertMode |
| Exclusion path unexpectedly matches | Request path in clientCertExclusionPaths | Remove only the exclusion entries no longer needed |
| HTTP instead of HTTPS | httpsOnly=false and callers use HTTP | Enable HTTPS-only and retest |
| PEM reconstruction bug | App treats base64 content as full PEM | Prepend and append PEM markers before parsing X-ARR-ClientCert |
| ASE ingress differs from multitenant | Behavior gap between test app and ASE app | Document the actual front-end trust boundary for ASE; do not assume public path parity |
8. Immediate Mitigations¶
- Enable
clientCertEnabledand set the intendedclientCertMode - Remove only the exclusion paths you no longer need
- Enforce HTTPS-only and retest with an actual client certificate
- Reconstruct PEM correctly before parsing
X-ARR-ClientCert - Validate the certificate chain and authorization policy in application code
- Correct outbound certificate thumbprint, path, or store lookup logic
- Review ASE ingress design and document the actual front-end trust boundary instead of assuming ASE behaves exactly like the public multitenant ingress path
9. Prevention¶
- Keep inbound and outbound mTLS runbooks separate
- add a lower-environment
/cert-infodiagnostics endpoint for rollout testing - document every
clientCertExclusionPathsentry with business justification - validate certificate loading and rotation during planned maintenance windows
- treat ASE as a topology change, not as a new certificate format
See Also¶
- Mutual TLS Architecture
- Incoming Client Certificates
- Outbound Client Certificates
- SSL Certificate Issues