Skip to main content

Theoretical Foundation: Configure Certificates and Transport Layer Security (TLS) for an App Service


1. Initial Intuition​

Imagine you're sending a letter with banking information through the mail. Without protection, anyone who intercepts the letter can read the content. To protect it, you use a sealed envelope and encode the message. The recipient has the key to decode it.

On the web, HTTPS with TLS works exactly like this: all communication between the user's browser and your server is encrypted, preventing third parties from reading or modifying data in transit. The TLS/SSL certificate is like an "authenticity seal" that proves to the browser that it's really talking to the legitimate server, not an impostor.

For an Azure App Service, configuring certificates and TLS means ensuring your application is accessed securely via HTTPS, that users see the green lock in the browser, and that no one can intercept sensitive data like passwords, card data, or personal information.


2. Context​

Where certificates and TLS fit in App Service security​

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

Why this matters for AZ-104​

Configuring TLS and certificates is one of the most common tasks for an Azure administrator managing App Services. It involves decisions about which type of certificate to use, how to configure custom domains with HTTPS, and how to ensure the TLS version is adequate for compliance and security.


3. Building the Concepts​

3.1 What is a TLS/SSL certificate​

A TLS certificate (formerly called SSL, but the SSL term is legacy and technically obsolete) has three functions:

  1. Identification: proves that the server is who it claims to be (validated by a CA)
  2. Encryption: enables key negotiation to encrypt communication
  3. Integrity: ensures data hasn't been modified in transit

The certificate contains:

  • The domain it applies to (Common Name / Subject Alternative Names)
  • The name of the entity that owns the certificate
  • The certificate authority (CA) that issued it
  • Validity date
  • The public key (the private key stays on the server, is never transmitted)

3.2 Types of certificates available in App Service​

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

App Service Managed Certificate (free):

  • Issued by DigiCert for Azure
  • Valid for 180 days, automatically renewed by Azure
  • Supports only subdomains (www.domain.com, api.domain.com)
  • Doesn't support root domain (domain.com) or wildcard (*.domain.com)
  • Cannot be exported (no private key available)
  • Requires the domain to already be mapped to the App Service

Custom Certificate (PFX/PEM):

  • You acquire from any CA (Let's Encrypt, DigiCert, Comodo, GlobalSign, etc.)
  • Supports wildcard and EV (Extended Validation)
  • Supports root domain
  • You're responsible for renewal
  • Upload in PFX (Windows) or PEM (Linux) format

App Service Certificate:

  • Product purchased directly in Azure
  • Automatically stored in Key Vault
  • Standard (single domain) or WildCard
  • Renewal can be configured as automatic
  • Natively integrated with App Service

Key Vault Certificate:

  • Certificate stored in Azure Key Vault
  • App Service accesses via Managed Identity
  • Centralizes certificate management for multiple apps
  • Supports automatic renewal via Key Vault integration with CAs

3.3 Certificate validation types​

TypeValidationTypical useIssue time
DV (Domain Validation)Only proves domain controlBlogs, APIs, internal appsMinutes
OV (Organization Validation)Also validates the organizationE-commerce, corporate portals1-3 days
EV (Extended Validation)Extensive organization validationBanks, governments, high credibility1-2 weeks

3.4 Wildcard certificates and SANs​

Wildcard: a single certificate for *.domain.com covers all subdomains: www, api, admin, staging, etc. But it doesn't cover the root domain domain.com or nested subdomains (sub.api.domain.com).

SAN (Subject Alternative Names): a certificate that explicitly lists multiple domains. For example: www.company.com, api.company.com, company.com, company.com.br.

3.5 TLS configuration​

App Service allows configuring:

Minimum TLS Version: the oldest TLS version the server accepts.

VersionStatusRecommendation
TLS 1.0Obsolete, known vulnerabilitiesNever use in production
TLS 1.1ObsoleteAvoid
TLS 1.2Widely supported, secureCurrently recommended minimum
TLS 1.3More secure and fasterRecommended for new systems

Cipher Suites: the cryptographic algorithms the server accepts to negotiate the TLS connection. App Service uses Azure's cipher suite list, which follows security best practices. For specific compliance (PCI-DSS, FIPS 140-2), restrictions may be necessary.

3.6 Client Certificates (mTLS)​

By default, only the server needs a certificate (one-way TLS). But in some high-security applications, the client also needs to present a certificate (mutual TLS or mTLS).

App Service supports client certificate requirement where each HTTP request must include a client certificate. App Service validates the certificate and passes it to the app via the X-ARR-ClientCert header.


4. Structural View​

HTTPS configuration flow with custom domain​

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

SNI SSL vs. IP-based SSL​

SNI SSL (Server Name Indication):

  • Multiple certificates can be associated with a single IP address
  • Supported by all modern browsers (>99.9% of users)
  • No additional cost in App Service
  • Recommended for the vast majority of cases

IP-based SSL:

  • A dedicated IP address for the certificate
  • Compatibility with very old clients that don't support SNI
  • Additional cost in App Service (charged per IP binding)
  • Rarely necessary today

5. Practical Operation​

Managed certificate lifecycle​

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

Non-obvious behaviors​

Managed Certificate doesn't work for root domain (naked domain). www.ecommerce.com.br is supported by Managed Certificate. ecommerce.com.br (without www) is not. For the root domain, you need a custom certificate or App Service Certificate.

TLS Binding must be recreated if certificate is manually renewed. For custom certificates (not managed), when you upload a new certificate (renewed), the old binding still points to the previous certificate. You need to update the binding to point to the new thumbprint.

App Service exposes the certificate to the app via header and environment variable. When a certificate is added to App Service (even if not used for HTTPS but for client authentication to an external service), Azure makes it available to the app via the WEBSITE_LOAD_CERTIFICATES environment variable with the thumbprint, and the app can access it via the OS certificate store.

HTTPS Only redirects but doesn't block. HTTPS Only sends an HTTP 301 redirect from HTTP to HTTPS. This means the first HTTP request still reaches the server before the redirect. For very high security applications, implement HSTS (HTTP Strict Transport Security) so the browser makes the request directly via HTTPS without trying HTTP first.

Certificates for internal app use vs. for endpoint TLS are different concepts. A certificate can be added to App Service for two purposes: (1) to terminate TLS at the app endpoint (which ensures HTTPS for users), or (2) for the app to use internally when connecting to other services that require certificate authentication. Both are managed in Certificates in the portal, but have different functions.


6. Implementation Methods​

Azure Portal​

When to use: initial configuration, visual management of certificates and bindings

To configure HTTPS with Managed Certificate:

  1. Portal > App Service > Custom domains > Add custom domain
  2. Verify domain ownership (CNAME or TXT)
  3. After adding domain: Portal > App Service > Certificates > Managed certificates
  4. + Add certificate > select the domain
  5. Wait for issuance (~a few minutes)
  6. Portal > App Service > Custom domains > next to domain, click lock/binding icon
  7. Select the newly issued certificate
  8. Add TLS/SSL Binding with SNI SSL type

To upload custom certificate (PFX):

  1. Portal > App Service > Certificates > Bring your own certificates (.pfx)
  2. + Add certificate
  3. Upload .pfx file + certificate password
  4. After upload, create TLS binding associating certificate with domain

To configure minimum TLS:

  1. Portal > App Service > TLS/SSL settings or Configuration > General settings
  2. Minimum Inbound TLS Version: select 1.2

Azure CLI​

# Create custom domain (prerequisite for any certificate)
# (requires CNAME or A record already configured in DNS)
az webapp config hostname add \
--resource-group "rg-webapp" \
--webapp-name "ecommerce-frontend" \
--hostname "www.ecommerce.com.br"

# List custom domains
az webapp config hostname list \
--resource-group "rg-webapp" \
--webapp-name "ecommerce-frontend" \
--output table

# Create Managed Certificate (free)
az webapp config ssl create \
--resource-group "rg-webapp" \
--name "ecommerce-frontend" \
--hostname "www.ecommerce.com.br"

# Check managed certificate status
az webapp config ssl list \
--resource-group "rg-webapp" \
--output table

# Create TLS binding (SNI SSL) with managed certificate
THUMBPRINT=$(az webapp config ssl list \
--resource-group "rg-webapp" \
--query "[?subjectName=='www.ecommerce.com.br'].thumbprint" \
--output tsv)

az webapp config ssl bind \
--resource-group "rg-webapp" \
--name "ecommerce-frontend" \
--certificate-thumbprint "$THUMBPRINT" \
--ssl-type SNI

# Upload custom PFX certificate
az webapp config ssl upload \
--resource-group "rg-webapp" \
--webapp-name "ecommerce-frontend" \
--certificate-file "./wildcard-certificate.pfx" \
--certificate-password "<pfx-password>"

# List available certificates (see thumbprints)
az webapp config ssl list \
--resource-group "rg-webapp" \
--output table

# Create TLS binding with custom certificate (PFX)
az webapp config ssl bind \
--resource-group "rg-webapp" \
--name "ecommerce-frontend" \
--certificate-thumbprint "<CERTIFICATE_THUMBPRINT>" \
--ssl-type SNI

# Enable HTTPS Only
az webapp update \
--resource-group "rg-webapp" \
--name "ecommerce-frontend" \
--https-only true

# Configure minimum TLS version
az webapp config set \
--resource-group "rg-webapp" \
--name "ecommerce-frontend" \
--min-tls-version 1.2

# Configure client certificates (mTLS)
az webapp update \
--resource-group "rg-webapp" \
--name "ecommerce-frontend" \
--set clientCertEnabled=true \
--set clientCertMode=Required

# Import certificate from Azure Key Vault
az webapp config ssl import \
--resource-group "rg-webapp" \
--name "ecommerce-frontend" \
--key-vault "kv-ecommerce-prod" \
--key-vault-certificate-name "wildcard-ecommerce"

# Remove TLS binding
az webapp config ssl unbind \
--resource-group "rg-webapp" \
--name "ecommerce-frontend" \
--certificate-thumbprint "<THUMBPRINT>" \
--ssl-type SNI

# Delete certificate
az webapp config ssl delete \
--resource-group "rg-webapp" \
--certificate-thumbprint "<THUMBPRINT>"

# View current TLS configuration
az webapp config show \
--resource-group "rg-webapp" \
--name "ecommerce-frontend" \
--query "{HTTPS_Only: httpsOnly, Min_TLS: minTlsVersion, ClientCert: clientCertEnabled}" \
--output json

Azure PowerShell​

# Upload PFX certificate
$pfxPassword = ConvertTo-SecureString "<password>" -AsPlainText -Force
New-AzWebAppSSLBinding `
-ResourceGroupName "rg-webapp" `
-WebAppName "ecommerce-frontend" `
-Name "www.ecommerce.com.br" `
-CertificateFilePath "./certificate.pfx" `
-CertificatePassword $pfxPassword `
-SslState SniEnabled

# Create binding with existing certificate
New-AzWebAppSSLBinding `
-ResourceGroupName "rg-webapp" `
-WebAppName "ecommerce-frontend" `
-Name "www.ecommerce.com.br" `
-Thumbprint "<THUMBPRINT>" `
-SslState SniEnabled

# List SSL bindings
Get-AzWebAppSSLBinding `
-ResourceGroupName "rg-webapp" `
-WebAppName "ecommerce-frontend"

# Configure HTTPS Only
Set-AzWebApp `
-ResourceGroupName "rg-webapp" `
-Name "ecommerce-frontend" `
-HttpsOnly $true

# Remove binding
Remove-AzWebAppSSLBinding `
-ResourceGroupName "rg-webapp" `
-WebAppName "ecommerce-frontend" `
-Name "www.ecommerce.com.br" `
-Force

Bicep​

// Custom certificate via PFX (base64 encoded)
resource certificate 'Microsoft.Web/certificates@2022-09-01' = {
name: 'cert-ecommerce-wildcard'
location: 'brazilsouth'
properties: {
pfxBlob: pfxBase64String // PFX in base64
password: pfxPassword
serverFarmId: appServicePlan.id
}
}

// Certificate from Key Vault
resource certFromKeyVault 'Microsoft.Web/certificates@2022-09-01' = {
name: 'cert-from-kv'
location: 'brazilsouth'
properties: {
keyVaultId: keyVault.id
keyVaultSecretName: 'wildcard-cert'
serverFarmId: appServicePlan.id
}
}

// App Service with TLS configured
resource webApp 'Microsoft.Web/sites@2022-09-01' = {
name: 'ecommerce-frontend'
location: 'brazilsouth'
properties: {
serverFarmId: appServicePlan.id
httpsOnly: true // HTTPS Only
siteConfig: {
minTlsVersion: '1.2' // TLS 1.2 minimum
ftpsState: 'Disabled' // Disable insecure FTP
http20Enabled: true // Enable HTTP/2
clientCertEnabled: false // mTLS disabled by default
}
// Hostname binding with SNI SSL
hostNameSslStates: [
{
name: 'www.ecommerce.com.br'
sslState: 'SniEnabled'
thumbprint: certificate.properties.thumbprint
}
]
}
}

7. Control and Security​

HSTS (HTTP Strict Transport Security)​

App Service's HTTPS Only redirects HTTP to HTTPS, but the first request still goes via HTTP before the redirect. HSTS instructs the browser to never try HTTP for that domain again after the first HTTPS visit.

HSTS is configured as an HTTP response header. In App Service, you can configure it via:

In application code (recommended for full control):

// .NET 8 - Program.cs
app.UseHsts();

Via web.config (Windows):

<configuration>
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Strict-Transport-Security"
value="max-age=31536000; includeSubDomains; preload" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>

Via Application Gateway or Front Door: for apps behind a gateway, configure the HSTS header on the gateway.

Certificate renewal and alerts​

For custom certificates (not managed), configure alerts before expiration:

# Check expiration date of all certificates
az webapp config ssl list \
--query "[].{Name: name, Domain: subjectName, Expires: expirationDate, Thumbprint: thumbprint}" \
--output table

# Via Resource Graph: certificates expiring in 30 days
az graph query -q "
Resources
| where type == 'microsoft.web/certificates'
| extend expiryDate = todatetime(properties.expirationDate)
| where expiryDate < ago(-30d) and expiryDate > now()
| project name, resourceGroup, expiryDate, thumbprint=properties.thumbprint"

Store certificates in Key Vault​

The safest practice is to never have PFX files locally in CI/CD pipelines. Use Key Vault as the source of truth:

# Import PFX to Key Vault
az keyvault certificate import \
--vault-name "kv-ecommerce-prod" \
--name "wildcard-ecommerce-cert" \
--file "./wildcard.pfx" \
--password "<pfx-password>"

# Configure automatic renewal in Key Vault (for supported CAs like DigiCert)
az keyvault certificate create \
--vault-name "kv-ecommerce-prod" \
--name "api-cert" \
--policy "$(az keyvault certificate get-default-policy)"

# App Service import from Key Vault
az webapp config ssl import \
--resource-group "rg-webapp" \
--name "ecommerce-frontend" \
--key-vault "kv-ecommerce-prod" \
--key-vault-certificate-name "wildcard-ecommerce-cert"

8. Decision Making​

Type of certificate to use​

SituationCertificate typeReason
Simple subdomain, zero costApp Service Managed (free)Automatic renewal, zero cost, zero configuration
Multiple subdomainsCustom wildcard or App Service Certificate WildcardOne certificate covers all
Root domain (naked domain)Custom certificate or App Service CertificateManaged doesn't support root domain
Organizational validation (OV/EV)Paid CA certificateManaged only issues DV
Multiple apps with same domainKey Vault CertificateCentralized certificate, one place to renew
Internal corporate APILet's Encrypt via custom certFree DV, renewable via automation

SNI SSL vs. IP-based SSL​

SituationChoiceReason
99% of modern casesSNI SSLNo additional cost, universal support
App with very legacy clients (IE6, some IoT)IP-based SSLSNI not supported on these clients
Requires fixed IP for app (e.g., for whitelist)IP-based SSLStatic IP associated with certificate

9. Best Practices​

Use App Service Managed Certificate whenever possible for simple subdomains. It's free, renews automatically, and requires no management. For most API and internal app subdomains, there's no reason to use paid certificates.

Store custom certificates in Key Vault, not in repositories or pipelines. A PFX certificate with its private key is a sensitive credential. Storing it in Key Vault centralizes management, allows access via Managed Identity without passwords, and facilitates auditing of who accessed the certificate.

Configure expiration alerts for custom certificates. Azure Monitor can alert when a certificate is nearing expiration. Configure alerts at 60 days and 30 days to have sufficient time to renew.

Enable HTTPS Only on every production app. It's a one-click configuration that ensures users never accidentally use HTTP. There's no reason not to enable it in production.

Configure TLS 1.2 as minimum; consider 1.3 for new systems. TLS 1.0 and 1.1 have known vulnerabilities. Every production app should require minimum TLS 1.2. For PCI-DSS 4.0 compliance, TLS 1.2 is the mandatory minimum.

For wildcard, consider App Service Certificate instead of Let's Encrypt. Let's Encrypt doesn't issue wildcards via HTTP-01 challenge (which App Service supports natively). The manual process of wildcard with Let's Encrypt is complex. App Service Certificate issues wildcard directly integrated.


10. Common Errors​

ErrorWhy it happensHow to avoid
Managed Certificate not issuedDomain verification failed (CNAME not configured correctly)Verify CNAME in DNS before requesting; wait for propagation
Certificate added but HTTPS doesn't workTLS binding not created after cert uploadAfter upload, create the binding associating cert to custom domain
Certificate expired in productionManual renewal forgottenConfigure 60-day alert; use Managed or Key Vault with auto renewal
SNI SSL but clients report cert errorLegacy clients don't support SNIUse IP-based SSL if legacy clients are a requirement
Managed Certificate doesn't work for apex domainTechnical limitation of the featureUse App Service Certificate or custom cert for root domain
TLS binding points to old cert after renewalThumbprint changed, binding not updatedUpdate binding after uploading renewed certificate
PFX with wrong password on uploadIncorrect password or wrong file formatVerify password and ensure correct PFX format before upload

The most critical error​

Not configuring expiration alerts for custom certificates. An expired certificate in production means all users see a security error in the browser and most abandon the site. The impact of an expired certificate for a few hours on an e-commerce site can be tens of thousands in lost sales. Alerts at 60 and 30 days, combined with documented renewal processes, are essential.


11. Operations and Maintenance​

Check status of all certificates​

# List certificates with expiration date
az webapp config ssl list \
--query "[].{
App: subject,
Domain: subjectName,
Expires: expirationDate,
Thumbprint: thumbprint,
Type: issuer
}" \
--output table

# Check if HTTPS Only is enabled on all production apps
az graph query -q "
Resources
| where type == 'microsoft.web/sites'
| where tags.Environment == 'Production'
| where properties.httpsOnly == false
| project name, resourceGroup, subscriptionId"

# Check minimum TLS version
az graph query -q "
Resources
| where type == 'microsoft.web/sites'
| extend minTls = tostring(properties.siteConfig.minTlsVersion)
| where minTls != '1.2' and minTls != '1.3'
| project name, resourceGroup, minTls"

Configure Azure Monitor Alert for expiring certificates​

# Alert when certificate expires in less than 30 days
az monitor alert create \
--name "cert-expiring-30days" \
--resource-group "rg-monitoring" \
--condition "count 'Microsoft.Web/certificates' where 'expirationDate' < '30d'" \
--action-group "ag-critical-alerts"

12. Integration and Automation​

Automatic Let's Encrypt certificate renewal via Logic App or Automation​

For organizations that prefer Let's Encrypt (free) but need renewal automation:

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

Deploy pipeline with certificate from Key Vault​

# GitHub Actions: deploy with certificate managed via Key Vault
steps:
- name: Login Azure
uses: azure/login@v2

- name: Import certificate from Key Vault to App Service
uses: azure/CLI@v2
with:
inlineScript: |
# Import cert from KV to app (automatically renews binding)
az webapp config ssl import \
--resource-group ${{ vars.RESOURCE_GROUP }} \
--name ${{ vars.APP_NAME }} \
--key-vault ${{ vars.KEY_VAULT_NAME }} \
--key-vault-certificate-name ${{ vars.CERT_NAME }}

# Update binding with new thumbprint
THUMBPRINT=$(az keyvault certificate show \
--vault-name ${{ vars.KEY_VAULT_NAME }} \
--name ${{ vars.CERT_NAME }} \
--query "x509ThumbprintHex" -o tsv)

az webapp config ssl bind \
--resource-group ${{ vars.RESOURCE_GROUP }} \
--name ${{ vars.APP_NAME }} \
--certificate-thumbprint $THUMBPRINT \
--ssl-type SNI

13. Final Summary​

Essential points:

  • App Service Managed Certificate is free, renews automatically, but supports only subdomains (not apex/root domain, not wildcard)
  • TLS binding is what associates a certificate to a custom domain on App Service; without the binding, the certificate exists but isn't used for HTTPS
  • SNI SSL allows multiple certificates on the same IP; IP-based SSL uses a dedicated IP per certificate (rarely necessary)
  • HTTPS Only redirects HTTP to HTTPS via 301; should be enabled on every production app
  • TLS 1.2 is the recommended minimum; TLS 1.0 and 1.1 should be disabled
  • Custom certificates should be stored in Key Vault for centralized and secure management

Critical differences:

  • Managed Certificate vs. Custom Certificate: Managed is free and automatic but limited; Custom allows wildcard, EV, root domain, but requires manual renewal management
  • DV vs. OV vs. EV: differ in the level of CA validation; DV is domain control only, OV validates the organization, EV performs extensive validation
  • TLS binding vs. Custom Domain: custom domain maps the DNS name to the app; TLS binding associates a certificate to the DNS name to enable HTTPS
  • SNI SSL vs. IP-based SSL: SNI is recommended for all modern cases; IP-based is only necessary for very legacy clients

What needs to be remembered for AZ-104:

  • Managed Certificate requires Basic tier or higher (doesn't work on Free)
  • Managed Certificate doesn't support root domain (apex) or wildcards
  • Certificate uploaded as PFX needs the password provided during upload
  • After upload or renewal of custom certificate, the TLS binding must be updated with the new thumbprint
  • az webapp config ssl create for Managed Certificate, az webapp config ssl upload for custom PFX, az webapp config ssl import for Key Vault
  • az webapp config ssl bind to create TLS binding associating cert to domain
  • mTLS (client certificates) is enabled via clientCertEnabled: true and the client certificate is passed to the app via X-ARR-ClientCert header