Configure TLS mutual authentication for Azure App Service

You can restrict access to your Azure App Service app by enabling different types of authentication for it. One way to do it is to request a client certificate when the client request is over TLS/SSL and validate the certificate. This mechanism is called TLS mutual authentication or client certificate authentication. This article shows how to set up your app to use client certificate authentication.


If you access your site over HTTP and not HTTPS, you will not receive any client certificate. So if your application requires client certificates, you should not allow requests to your application over HTTP.

Prepare your web app

To create custom security bindings or enable client certificates for your App Service app, your App Service plan must be in the Basic, Standard, Premium, or Isolated tier. In this step, you make sure that your web app is in the supported pricing tier.

Sign in to Azure

Open the Azure portal.

Search for and select App Services.

Select App Services

On the App Services page, select the name of your web app.

Screenshot of the App Services page in Azure portal showing a list of all running web apps, with the first app in the list highlighted.

You have landed on the management page of your web app.

Check the pricing tier

In the left-hand navigation of your web app page, scroll to the Settings section and select Scale up (App Service plan).

Scale-up menu

Check to make sure that your web app is not in the F1 or D1 tier. Your web app's current tier is highlighted by a dark blue box.

Check pricing tier

Custom SSL is not supported in the F1 or D1 tier. If you need to scale up, follow the steps in the next section. Otherwise, close the Scale up page and skip the Scale up your App Service plan section.

Scale up your App Service plan

Select any of the non-free tiers (B1, B2, B3, or any tier in the Production category). For additional options, click See additional options.

Click Apply.

Choose pricing tier

When you see the following notification, the scale operation is complete.

Scale up notification

Enable client certificates

To set up your app to require client certificates, you can switch On the Require incoming certificate by selecting Configuration > General Settings from the Azure Portal or you need to set the clientCertEnabled setting for your app to true. To set the setting, run the following command in the Cloud Shell.

az webapp update --set clientCertEnabled=true --name <app_name> --resource-group <group_name>

Exclude paths from requiring authentication

When you enable mutual auth for your application, all paths under the root of your app will require a client certificate for access. To allow certain paths to remain open for anonymous access, you can define exclusion paths as part of your application configuration.

Exclusion paths can be configured by selecting Configuration > General Settings and defining an exclusion path. In this example, anything under /public path for your application would not request a client certificate.

Certificate Exclusion Paths

Access client certificate

In App Service, TLS termination of the request happens at the frontend load balancer. When forwarding the request to your app code with client certificates enabled, App Service injects an X-ARR-ClientCert request header with the client certificate. App Service does not do anything with this client certificate other than forwarding it to your app. Your app code is responsible for validating the client certificate.

For ASP.NET, the client certificate is available through the HttpRequest.ClientCertificate property.

For other application stacks (Node.js, PHP, etc.), the client cert is available in your app through a base64 encoded value in the X-ARR-ClientCert request header.

ASP.NET sample

    using System;
    using System.Collections.Specialized;
    using System.Security.Cryptography.X509Certificates;
    using System.Web;

    namespace ClientCertificateUsageSample
        public partial class Cert : System.Web.UI.Page
            public string certHeader = "";
            public string errorString = "";
            private X509Certificate2 certificate = null;
            public string certThumbprint = "";
            public string certSubject = "";
            public string certIssuer = "";
            public string certSignatureAlg = "";
            public string certIssueDate = "";
            public string certExpiryDate = "";
            public bool isValidCert = false;

            // Read the certificate from the header into an X509Certificate2 object
            // Display properties of the certificate on the page
            protected void Page_Load(object sender, EventArgs e)
                NameValueCollection headers = base.Request.Headers;
                certHeader = headers["X-ARR-ClientCert"];
                if (!String.IsNullOrEmpty(certHeader))
                        byte[] clientCertBytes = Convert.FromBase64String(certHeader);
                        certificate = new X509Certificate2(clientCertBytes);
                        certSubject = certificate.Subject;
                        certIssuer = certificate.Issuer;
                        certThumbprint = certificate.Thumbprint;
                        certSignatureAlg = certificate.SignatureAlgorithm.FriendlyName;
                        certIssueDate = certificate.NotBefore.ToShortDateString() + " " + certificate.NotBefore.ToShortTimeString();
                        certExpiryDate = certificate.NotAfter.ToShortDateString() + " " + certificate.NotAfter.ToShortTimeString();
                    catch (Exception ex)
                        errorString = ex.ToString();
                        isValidCert = IsValidClientCertificate();
                        if (!isValidCert) Response.StatusCode = 403;
                        else Response.StatusCode = 200;
                    certHeader = "";

            // This is a SAMPLE verification routine. Depending on your application logic and security requirements, 
            // you should modify this method
            private bool IsValidClientCertificate()
                // In this example we will only accept the certificate as a valid certificate if all the conditions below are met:
                // 1. The certificate is not expired and is active for the current time on server.
                // 2. The subject name of the certificate has the common name nildevecc
                // 3. The issuer name of the certificate has the common name nildevecc and organization name Microsoft Corp
                // 4. The thumbprint of the certificate is 30757A2E831977D8BD9C8496E4C99AB26CB9622B
                // This example does NOT test that this certificate is chained to a Trusted Root Authority (or revoked) on the server 
                // and it allows for self signed certificates

                if (certificate == null || !String.IsNullOrEmpty(errorString)) return false;

                // 1. Check time validity of certificate
                if (DateTime.Compare(DateTime.Now, certificate.NotBefore) < 0 || DateTime.Compare(DateTime.Now, certificate.NotAfter) > 0) return false;

                // 2. Check subject name of certificate
                bool foundSubject = false;
                string[] certSubjectData = certificate.Subject.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (string s in certSubjectData)
                    if (String.Compare(s.Trim(), "CN=nildevecc") == 0)
                        foundSubject = true;
                if (!foundSubject) return false;

                // 3. Check issuer name of certificate
                bool foundIssuerCN = false, foundIssuerO = false;
                string[] certIssuerData = certificate.Issuer.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (string s in certIssuerData)
                    if (String.Compare(s.Trim(), "CN=nildevecc") == 0)
                        foundIssuerCN = true;
                        if (foundIssuerO) break;

                    if (String.Compare(s.Trim(), "O=Microsoft Corp") == 0)
                        foundIssuerO = true;
                        if (foundIssuerCN) break;

                if (!foundIssuerCN || !foundIssuerO) return false;

                // 4. Check thumprint of certificate
                if (String.Compare(certificate.Thumbprint.Trim().ToUpper(), "30757A2E831977D8BD9C8496E4C99AB26CB9622B") != 0) return false;

                return true;

Node.js sample

The following Node.js sample code gets the X-ARR-ClientCert header and uses node-forge to convert the base64-encoded PEM string into a certificate object and validate it:

import { NextFunction, Request, Response } from 'express';
import { pki, md, asn1 } from 'node-forge';

export class AuthorizationHandler {
    public static authorizeClientCertificate(req: Request, res: Response, next: NextFunction): void {
        try {
            // Get header
            const header = req.get('X-ARR-ClientCert');
            if (!header) throw new Error('UNAUTHORIZED');

            // Convert from PEM to pki.CERT
            const pem = `-----BEGIN CERTIFICATE-----${header}-----END CERTIFICATE-----`;
            const incomingCert: pki.Certificate = pki.certificateFromPem(pem);

            // Validate certificate thumbprint
            const fingerPrint = md.sha1.create().update(asn1.toDer(pki.certificateToAsn1(incomingCert)).getBytes()).digest().toHex();
            if (fingerPrint.toLowerCase() !== 'abcdef1234567890abcdef1234567890abcdef12') throw new Error('UNAUTHORIZED');

            // Validate time validity
            const currentDate = new Date();
            if (currentDate < incomingCert.validity.notBefore || currentDate > incomingCert.validity.notAfter) throw new Error('UNAUTHORIZED');

            // Validate issuer
            if (incomingCert.issuer.hash.toLowerCase() !== 'abcdef1234567890abcdef1234567890abcdef12') throw new Error('UNAUTHORIZED');

            // Validate subject
            if (incomingCert.subject.hash.toLowerCase() !== 'abcdef1234567890abcdef1234567890abcdef12') throw new Error('UNAUTHORIZED');

        } catch (e) {
            if (e instanceof Error && e.message === 'UNAUTHORIZED') {
            } else {

Java sample

The following Java class encodes the certificate from X-ARR-ClientCert to an X509Certificate instance. certificateIsValid() validates that the certificate's thumbprint matches the one given in the constructor and that certificate has not expired.



import javax.xml.bind.DatatypeConverter;
import java.util.Base64;
import java.util.Date;

public class ClientCertValidator { 

    private String thumbprint;
    private X509Certificate certificate;

     * Constructor.
     * @param certificate The certificate from the "X-ARR-ClientCert" HTTP header
     * @param thumbprint The thumbprint to check against
     * @throws CertificateException If the certificate factory cannot be created.
    public ClientCertValidator(String certificate, String thumbprint) throws CertificateException {
        certificate = certificate
                .replaceAll(X509Factory.BEGIN_CERT, "")
                .replaceAll(X509Factory.END_CERT, "");
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        byte [] base64Bytes = Base64.getDecoder().decode(certificate);
        X509Certificate X509cert =  (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(base64Bytes));


     * Check that the certificate's thumbprint matches the one given in the constructor, and that the
     * certificate has not expired.
     * @return True if the certificate's thumbprint matches and has not expired. False otherwise.
    public boolean certificateIsValid() throws NoSuchAlgorithmException, CertificateEncodingException {
        return certificateHasNotExpired() && thumbprintIsValid();

     * Check certificate's timestamp.
     * @return Returns true if the certificate has not expired. Returns false if it has expired.
    private boolean certificateHasNotExpired() {
        Date currentTime = new java.util.Date();
        try {
        } catch (CertificateExpiredException | CertificateNotYetValidException e) {
            return false;
        return true;

     * Check the certificate's thumbprint matches the given one.
     * @return Returns true if the thumbprints match. False otherwise.
    private boolean thumbprintIsValid() throws NoSuchAlgorithmException, CertificateEncodingException {
        MessageDigest md = MessageDigest.getInstance("SHA-1");
        byte[] der = this.getCertificate().getEncoded();
        byte[] digest = md.digest();
        String digestHex = DatatypeConverter.printHexBinary(digest);
        return digestHex.toLowerCase().equals(this.getThumbprint().toLowerCase());

    // Getters and setters

    public void setThumbprint(String thumbprint) {
        this.thumbprint = thumbprint;

    public String getThumbprint() {
        return this.thumbprint;

    public X509Certificate getCertificate() {
        return certificate;

    public void setCertificate(X509Certificate certificate) {
        this.certificate = certificate;