# Azure Front Door

<Image
  src={azureIcon}
  alt="Azure Front Door logo"
  width={100}
  height={100}
  class:list={'float-inline-left icon'}
  data-zoom-off
/>

[Azure Front Door](https://learn.microsoft.com/azure/frontdoor/) is a global, scalable entry point that uses Microsoft's edge network to deliver fast, secure, and highly available web applications. The Aspire Azure Front Door hosting integration provisions a Front Door profile and routes traffic to your backends.

## Hosting integration

The Aspire Azure Front Door hosting integration models the resource as the following type:

- `AzureFrontDoorResource`: Represents an Azure Front Door profile, including its endpoints, origin groups, origins, and routes.

To access this type and APIs, add the [📦 Aspire.Hosting.Azure.FrontDoor](https://www.nuget.org/packages/Aspire.Hosting.Azure.FrontDoor) NuGet package to your AppHost project.

<InstallPackage packageName="Aspire.Hosting.Azure.FrontDoor" />

### Add an Azure Front Door resource

In your AppHost, call `addAzureFrontDoor` to add a Front Door profile, then call `withOrigin` to attach a backend resource as an origin:

```csharp title="AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

builder.AddAzureAppServiceEnvironment("production");

var api = builder.AddProject<Projects.Api>("api")
    .WithExternalHttpEndpoints();

builder.AddAzureFrontDoor("frontdoor")
    .WithOrigin(api);

// After adding all resources, run the app...
builder.Build().Run();
```

```typescript title="apphost.mts"
import { createBuilder } from './.aspire/modules/aspire.mjs';

const builder = await createBuilder();

await builder.addAzureAppServiceEnvironment("production");

const api = await builder.addProject("api", "../Api/Api.csproj");
await api.withExternalHttpEndpoints();

const frontdoor = await builder.addAzureFrontDoor("frontdoor");
await frontdoor.withOrigin(api);

// After adding all resources, run the app...
await builder.build().run();
```

The preceding code:

- Adds an Azure App Service environment (a compute environment is required so Front Door can resolve the origin's hostname).
- Adds an `api` project and exposes it externally with `withExternalHttpEndpoints`. Front Door requires an origin to expose an external HTTP or HTTPS endpoint.
- Adds a Front Door profile named `frontdoor` and configures the `api` project as an origin.

Each call to `withOrigin` creates a separate Front Door endpoint, origin group, origin, and route, so each backend is independently routable through its own `*.azurefd.net` hostname.
**Caution:** When you call `addAzureFrontDoor`, it implicitly calls
  `addAzureProvisioning`—which adds support for generating Azure resources
  dynamically during app startup. The app must configure the appropriate
  subscription and location. For more information, see [Local provisioning:
  Configuration](/integrations/cloud/azure/local-provisioning/#configuration).

### Add multiple origins

You can add multiple backends to the same Front Door profile. Each origin gets its own Front Door endpoint and route:

```csharp title="AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

builder.AddAzureAppServiceEnvironment("production");

var api = builder.AddProject<Projects.Api>("api")
    .WithExternalHttpEndpoints();

var web = builder.AddProject<Projects.Web>("web")
    .WithExternalHttpEndpoints();

builder.AddAzureFrontDoor("frontdoor")
    .WithOrigin(api)
    .WithOrigin(web);

builder.Build().Run();
```

```typescript title="apphost.mts"
import { createBuilder } from './.aspire/modules/aspire.mjs';

const builder = await createBuilder();

await builder.addAzureAppServiceEnvironment("production");

const api = await builder.addProject("api", "../Api/Api.csproj");
await api.withExternalHttpEndpoints();

const web = await builder.addProject("web", "../Web/Web.csproj");
await web.withExternalHttpEndpoints();

const frontdoor = await builder.addAzureFrontDoor("frontdoor");
await frontdoor.withOrigin(api);
await frontdoor.withOrigin(web);

await builder.build().run();
```

Each origin must be added at most once per Front Door resource — adding the same resource twice throws `InvalidOperationException`.

### Health probes

Front Door uses health probes to determine which origins are healthy. By default, the Aspire integration probes the origin's external endpoint at path `/` over HTTPS. If you have configured an HTTP probe on the origin (for example with `WithHttpProbe`), Front Door uses the path and protocol from the resource's liveness probe instead.

### Provisioning-generated Bicep

When you publish your app, Aspire generates Bicep alongside the manifest. A Front Door resource with a single origin generates Bicep similar to the following:
**Caution:** **Upgrading from Aspire 13.3.0-preview:** The resource names generated for Front Door profiles, endpoints, origin groups, origins, and routes changed in Aspire 13.4. This can result in duplicate Azure resources (profiles, endpoints, origin groups, routes, and origins) during an upgrade. To avoid duplicates, either remove the existing Azure resources before redeploying, or set resource names explicitly using `ConfigureInfrastructure`.

```bicep title="Generated Bicep — frontdoor.bicep"
@description('The location for the resource(s) to be deployed.')
param location string = resourceGroup().location

param my_api_host string

resource frontdoor 'Microsoft.Cdn/profiles@2025-06-01' = {
  name: take('frontdoor-${uniqueString(resourceGroup().id)}', 260)
  location: 'Global'
  sku: {
    name: 'Standard_AzureFrontDoor'
  }
  tags: {
    'aspire-resource-name': 'frontdoor'
  }
}

resource my_apiEndpoint 'Microsoft.Cdn/profiles/afdEndpoints@2025-06-01' = {
  name: take('myapiEndpoint-${uniqueString(resourceGroup().id)}', 46)
  location: 'Global'
  parent: frontdoor
}

resource my_apiOriginGroup 'Microsoft.Cdn/profiles/originGroups@2025-06-01' = {
  name: take('myapiOriginGroup-${uniqueString(resourceGroup().id)}', 90)
  properties: {
    healthProbeSettings: {
      probePath: '/'
      probeProtocol: 'Https'
    }
    loadBalancingSettings: {
      sampleSize: 4
      successfulSamplesRequired: 3
      additionalLatencyInMilliseconds: 50
    }
  }
  parent: frontdoor
}

resource my_apiOrigin 'Microsoft.Cdn/profiles/originGroups/origins@2025-06-01' = {
  name: take('myapiOrigin-${uniqueString(resourceGroup().id)}', 90)
  properties: {
    hostName: my_api_host
    originHostHeader: my_api_host
  }
  parent: my_apiOriginGroup
}

resource my_apiRoute 'Microsoft.Cdn/profiles/afdEndpoints/routes@2025-06-01' = {
  name: take('myapiRoute-${uniqueString(resourceGroup().id)}', 90)
  properties: {
    forwardingProtocol: 'HttpsOnly'
    httpsRedirect: 'Enabled'
    linkToDefaultDomain: 'Enabled'
    originGroup: {
      id: my_apiOriginGroup.id
    }
    patternsToMatch: [
      '/*'
    ]
  }
  parent: my_apiEndpoint
  dependsOn: [
    my_apiOrigin
  ]
}

output my_api_endpointUrl string = 'https://${my_apiEndpoint.properties.hostName}'
```

Aspire passes the origin's hostname (resolved from the compute environment, not the full URL) into the `my_api_host` parameter so the Front Door origin points at the deployed backend.

The generated Bicep is a starting point and is influenced by changes to the provisioning infrastructure in C#. Customizations to the Bicep file directly are overwritten, so make changes through the provisioning APIs to ensure they're reflected in the generated files.

#### Customize provisioning infrastructure

For advanced scenarios such as upgrading to the Premium SKU, attaching a Web Application Firewall (WAF) policy, configuring caching, or tuning routes, use `ConfigureInfrastructure` (`configureInfrastructure` in TypeScript) to modify the generated Bicep model directly:

```csharp title="AppHost.cs"
using Azure.Provisioning.Cdn;

var frontDoor = builder.AddAzureFrontDoor("frontdoor")
    .WithOrigin(api)
    .ConfigureInfrastructure(infra =>
    {
        var resources = infra.GetProvisionableResources();

        // Upgrade to Premium SKU (required for WAF).
        var profile = resources.OfType<CdnProfile>().Single();
        profile.SkuName = CdnSkuName.PremiumAzureFrontDoor;

        // Enable caching on the route.
        var route = resources.OfType<FrontDoorRoute>().Single();
        route.CacheConfiguration = new FrontDoorRouteCacheConfiguration
        {
            QueryStringCachingBehavior = FrontDoorQueryStringCachingBehavior.IgnoreQueryString
        };
    });
```

```typescript title="apphost.mts"
import { createBuilder } from './.aspire/modules/aspire.mjs';

const builder = await createBuilder();

await builder.addAzureAppServiceEnvironment("production");

const api = await builder.addProject("api", "../Api/Api.csproj");
await api.withExternalHttpEndpoints();

const frontdoor = await builder.addAzureFrontDoor("frontdoor");
await frontdoor.withOrigin(api);
await frontdoor.configureInfrastructure(async (infra) => {
    // Use the AzureResourceInfrastructure API to customize Bicep
    // parameters or resources directly.
    await infra.addParameter("skuName", "Premium_AzureFrontDoor");
});

await builder.build().run();
```

For more information, see [Customize Azure resources](/integrations/cloud/azure/customize-resources/). For the full list of configurable properties, see the [Azure.Provisioning.Cdn](https://learn.microsoft.com/dotnet/api/azure.provisioning.cdn) and [Azure.Provisioning.FrontDoor](https://learn.microsoft.com/dotnet/api/azure.provisioning.frontdoor) API documentation.

## See also

- [Azure Front Door documentation](https://learn.microsoft.com/azure/frontdoor/)
- [Azure Front Door tier comparison](https://learn.microsoft.com/azure/frontdoor/standard-premium/tier-comparison)
- [Azure App Service integration](/integrations/cloud/azure/azure-app-service/azure-app-service-host/)
- [Azure integration overview](/integrations/cloud/azure/overview/)