Custom Domains and Certificates¶
This recipe shows production custom domain + TLS certificate binding for HTTP-triggered Node.js v4 APIs.
Architecture¶
flowchart TD
DNS[Public DNS CNAME or A Record] --> DOMAIN[Custom Domain]
DOMAIN --> TLS[TLS Certificate Binding]
TLS --> APP[Function App HTTPS Endpoint]
APP --> API[HTTP Trigger Handler] Prerequisites¶
Use extension bundle v4 in host.json:
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.*, 5.0.0)"
}
}
Create a function app for custom domain binding:
az group create --name $RG --location $LOCATION
az storage account create \
--name $STORAGE_NAME \
--resource-group $RG \
--location $LOCATION \
--sku Standard_LRS
az functionapp create \
--name $APP_NAME \
--resource-group $RG \
--storage-account $STORAGE_NAME \
--plan $PLAN_NAME \
--runtime node \
--runtime-version 20 \
--functions-version 4
| CLI element | Explanation |
|---|---|
| Command(s) | az group create, az storage account create, az functionapp create |
| Key flags | --name, --location, --resource-group, --sku, --storage-account, --plan, --runtime, --runtime-version, --functions-version |
| Variables | $RG, $LOCATION, $STORAGE_NAME, $APP_NAME, $PLAN_NAME |
| Expected result | Azure CLI returns provisioning details; confirm the resource name and successful provisioning state before continuing. |
Add a host name and upload/bind certificate:
az functionapp config hostname add \
--webapp-name $APP_NAME \
--resource-group $RG \
--hostname api.contoso.com
az functionapp config ssl upload \
--name $APP_NAME \
--resource-group $RG \
--certificate-file ./certs/api-contoso.pfx \
--certificate-password <pfx-password>
az functionapp config ssl bind \
--name $APP_NAME \
--resource-group $RG \
--certificate-thumbprint <thumbprint-from-upload> \
--ssl-type SNI
| CLI element | Explanation |
|---|---|
| Command(s) | az functionapp config hostname add, az functionapp config ssl upload, az functionapp config ssl bind |
| Key flags | --webapp-name, --resource-group, --hostname, --name, --certificate-file, --certificate-password, --certificate-thumbprint, --ssl-type |
| Variables | $APP_NAME, $RG |
| Expected result | Azure CLI applies the configuration change; confirm the returned JSON or follow-up query shows the expected value. |
Set HTTPS-only mode:
| CLI element | Explanation |
|---|---|
| Command(s) | az functionapp update |
| Key flags | --name, --resource-group, --set |
| Variables | $APP_NAME, $RG |
| Expected result | Azure CLI applies the configuration change; confirm the returned JSON or follow-up query shows the expected value. |
Important hosting plan note:
- Flex Consumption does not support App Service managed/platform certificates.
- For Flex Consumption, use uploaded certificates or external TLS termination.
Working Node.js v4 Code¶
const { app } = require("@azure/functions");
app.http("healthz", {
methods: ["GET"],
route: "healthz",
authLevel: "anonymous",
handler: async (request) => {
return {
status: 200,
headers: {
"content-type": "application/json",
"strict-transport-security": "max-age=31536000; includeSubDomains"
},
jsonBody: {
host: request.headers.get("host"),
status: "ok",
checkedUtc: new Date().toISOString()
}
};
}
});
Implementation Notes¶
- Validate DNS ownership before hostname binding to avoid failed certificate issuance.
- Keep endpoint-level health checks on custom domains after each certificate rotation.
- Enforce HTTPS globally (
httpsOnly=true) and add HSTS on responses. - Track certificate expiration dates and rotate before renewal windows close.