Back to blog
AzureBest PracticesCloud SecurityIdentity & Access

Azure App Service Client Certificate Not Required: Why It Matters and How to Fix It

Learn why requiring client certificates on Azure App Service matters, the risks of disabled mutual TLS, and step-by-step CLI, Bicep, and Terraform fixes.

TL;DR

This check flags Azure App Services that do not require client certificates, meaning mutual TLS is off and the app accepts any client without verifying its identity. Turn it on by setting clientCertEnabled to true (or mode Required) and validate certificates in your app code.

Most teams lock down inbound traffic with network rules, IP restrictions, and bearer tokens. That covers a lot, but it leaves a gap that mutual TLS was designed to close: proving who the caller actually is at the transport layer. When an Azure App Service does not require client certificates, anyone who can reach the endpoint can complete a TLS handshake without presenting any identity. The App Service Client Certificate Not Required check catches exactly this configuration.

This is not a problem for every app. A public marketing site does not need client certificates. But for internal APIs, partner integrations, and service-to-service traffic, mutual TLS is often a requirement rather than a nice-to-have, and a disabled setting here usually means someone forgot to flip it on.


What this check detects

The check inspects the clientCertEnabled property (and the related clientCertMode) on your Azure App Service. If client certificates are not required, the check fails.

App Service supports three client certificate modes:

  • Required — every request must present a valid client certificate or the request is rejected at the front end.
  • Optional — the server requests a certificate but does not reject requests that omit one. Your app code decides what to do.
  • Optional Interactive User — browsers are prompted for a certificate, but absence does not block the request.

Note: App Service terminates TLS at the front-end load balancer. When client certificates are enabled, the front end forwards the client certificate to your app in the X-ARR-ClientCert request header as a base64-encoded value. Your application is responsible for parsing and validating it.

This check looks for the strongest posture, where a certificate is required. An app with the mode set to Optional still depends entirely on application code to enforce identity, which is easy to get wrong, so the safe default for sensitive endpoints is Required.


Why it matters

Mutual TLS gives you cryptographic assurance of the client's identity before any application logic runs. Without it, you fall back to identity signals that live higher up the stack, and those are easier to forge, replay, or strip.

Real-world risk scenarios

  • Exposed internal APIs. An App Service meant for backend-to-backend traffic ends up reachable on the public internet. Without required client certificates, the only thing standing between an attacker and your API is whatever auth your code happens to enforce.
  • Token theft and replay. Bearer tokens can be stolen from logs, browser storage, or a compromised dependency. A stolen token works from anywhere. A client certificate is far harder to exfiltrate and use, especially when the private key is stored in an HSM or Key Vault.
  • Partner integrations. Many B2B contracts and compliance frameworks require mutual TLS for data exchange. A disabled setting can mean a failed audit or a broken contractual obligation.
  • Defense in depth gaps. If a WAF rule or IP allowlist is misconfigured, client certificates act as a second, independent gate. Remove that gate and a single mistake exposes the whole app.

Warning: Enabling required client certificates will block any caller that does not present a valid certificate, including health checks, monitoring probes, and existing clients. Roll this out carefully and confirm every legitimate caller is provisioned with a certificate first.

For regulated workloads, compliance frameworks such as PCI DSS, HIPAA, and ISO 27001 expect strong authentication on systems handling sensitive data. Mutual TLS is a direct, demonstrable control that maps to those requirements.


How to fix it

You can require client certificates through the Azure Portal, the CLI, or infrastructure as code. Pick whichever matches how you manage the rest of your environment.

Option 1: Azure Portal

  1. Open your App Service in the Azure Portal.
  2. Go to Settings > Configuration, then the General settings tab.
  3. Find Client certificate mode and set it to Required.
  4. Click Save and let the app restart.

Option 2: Azure CLI

Warning: The command below changes a live setting and triggers an app restart. Run it during a maintenance window, or against a staging slot first, to avoid dropping in-flight requests.

az webapp update \
  --resource-group myResourceGroup \
  --name myAppService \
  --set clientCertEnabled=true clientCertMode=Required

Verify the change:

az webapp show \
  --resource-group myResourceGroup \
  --name myAppService \
  --query "{enabled:clientCertEnabled, mode:clientCertMode}"

You should see:

{
  "enabled": true,
  "mode": "Required"
}

Option 3: Bicep

resource appService 'Microsoft.Web/sites@2023-12-01' = {
  name: 'myAppService'
  location: location
  properties: {
    serverFarmId: appServicePlanId
    httpsOnly: true
    clientCertEnabled: true
    clientCertMode: 'Required'
  }
}

Option 4: Terraform

resource "azurerm_linux_web_app" "example" {
  name                          = "myAppService"
  resource_group_name           = azurerm_resource_group.example.name
  location                      = azurerm_resource_group.example.location
  service_plan_id               = azurerm_service_plan.example.id
  https_only                    = true
  client_certificate_enabled    = true
  client_certificate_mode       = "Required"

  site_config {}
}

Validate the certificate in your app

Enabling the setting forces clients to present a certificate, but App Service does not validate the certificate's contents for you. Your app still needs to check the thumbprint, issuer, subject, and expiry. Here is a minimal ASP.NET Core example reading the forwarded header:

var certHeader = Request.Headers["X-ARR-ClientCert"].FirstOrDefault();
if (string.IsNullOrEmpty(certHeader))
{
    return Unauthorized();
}

var bytes = Convert.FromBase64String(certHeader);
var clientCert = new X509Certificate2(bytes);

var allowedThumbprint = "A1B2C3...";
if (!clientCert.Thumbprint.Equals(allowedThumbprint, StringComparison.OrdinalIgnoreCase)
    || DateTime.UtcNow > clientCert.NotAfter)
{
    return Unauthorized();
}

Tip: Validate against the issuer and subject rather than a single thumbprint when you rotate certificates frequently. Pinning a thumbprint means every rotation requires a code or config change, which tends to cause outages when a cert expires unexpectedly.


How to prevent it from happening again

Fixing one app is easy. Stopping the misconfiguration from reappearing across dozens of app services is the real win. Bake the control into the platform so nobody has to remember it.

Azure Policy

Use a policy that audits or denies app services without required client certificates. A custom policy definition targeting the property looks like this:

{
  "policyRule": {
    "if": {
      "allOf": [
        {
          "field": "type",
          "equals": "Microsoft.Web/sites"
        },
        {
          "anyOf": [
            {
              "field": "Microsoft.Web/sites/clientCertEnabled",
              "notEquals": "true"
            },
            {
              "field": "Microsoft.Web/sites/clientCertMode",
              "notEquals": "Required"
            }
          ]
        }
      ]
    },
    "then": {
      "effect": "[parameters('effect')]"
    }
  }
}

Start with the audit effect to measure your exposure, then move to deny for new resources once you have cleaned up the existing fleet.

Tip: Scope the deny policy with a tag like requires-mtls=true so it only applies to app services that genuinely need client certificates. This avoids breaking public-facing sites where mutual TLS makes no sense.

CI/CD gates

Catch the problem before it ships by scanning IaC in the pipeline. Tools like Checkov, tfsec, and Trivy can flag a Terraform plan with client certificates disabled. A simple gate in a GitHub Actions or Azure DevOps pipeline:

checkov -d ./infra --framework terraform \
  --check CKV_AZURE_46

Fail the build on a non-zero exit code so a pull request cannot merge while the setting is wrong.

Continuous monitoring with Lensix

Pipelines only catch what flows through them. Manual portal changes, drift, and resources created outside IaC slip past. Lensix runs the appservice_noclientcert check continuously across your subscriptions and surfaces every app service that drops below the required posture, so you find out about drift in hours instead of at audit time.


Best practices

  • Require, do not just request. Use Required mode for any internal or partner-facing API. Optional pushes all enforcement onto your code, where a single missed check defeats the control.
  • Pair mTLS with HTTPS-only. Set httpsOnly to true so traffic cannot downgrade to plain HTTP and bypass certificate handling entirely.
  • Store private keys securely. Keep client certificate private keys in Azure Key Vault or an HSM, never in source control or app settings.
  • Plan for rotation. Certificates expire. Automate issuance and renewal, and validate by issuer or subject so rotations do not require code changes.
  • Layer your controls. Combine mutual TLS with IP restrictions, a WAF, and identity-based auth. No single control should be the only thing protecting a sensitive endpoint.
  • Test the failure path. Confirm that a request without a valid certificate is actually rejected. A setting that looks enabled but is silently bypassed is worse than no setting at all.

Requiring client certificates is a small configuration change with an outsized security payoff for the right workloads. Turn it on where it belongs, enforce it with policy, and monitor for drift so it stays on.