You wouldn’t ask your local barista to start pouring pints behind the pub bar. The same goes for security controls in Azure. Azure Firewall and Application Gateway WAF are both vital, but they serve different purposes. Get them confused and you’ll either overspend or under‑protect.
What is Application Gateway WAF?
Application Gateway (AppGW) is a Layer 7 load balancer with optional WAF (Web Application Firewall) capabilities. Think of it as traffic control at the HTTP/S level. The WAF inspects requests bound for web apps, protecting against OWASP Top 10 threats like SQLi and XSS.
Key features include:
- Reverse proxy with SSL offload, end-to-end TLS, and SNI support
- WAF in Detection or Prevention mode (based on OWASP rulesets)
- HTTP header rewrites, redirects, URL-based routing
- Autoscaling and zone redundancy for resilience
It doesn’t replace Azure Firewall. Instead, it complements it.
How They Differ: AppGW WAF vs Azure Firewall
-
Azure Firewall:
- L3–L7 controls
- East-west + north-south traffic
- TLS inspection, IDPS, Threat Intel
- Network-centric enforcement
-
App Gateway WAF:
- L7, application-specific inspection
- Inbound HTTP/S (north-south only)
- OWASP rule protection for web apps
- Application-centric enforcement
In short: Firewall protects networks, WAF protects apps.
Deployment Models
Two options exist, each used in production depending on needs:
-
Side-by-Side
- Azure Firewall secures all traffic centrally.
- Application Gateway WAF directly fronts the web apps.
- Internet → App Gateway WAF → Web App (spoke)
- Outbound/other spoke traffic → Firewall
-
Chained
- Azure Firewall sits in front of App Gateway.
- Firewall does ingress filtering, threat intel, TLS inspection.
- Clean traffic is then forwarded to App GW for WAF and app‑layer routing.
- Internet → Firewall → App Gateway WAF → Web App
flowchart TD
Internet --> FW[Azure Firewall Premium]
FW --> AGW[Application Gateway + WAF]
AGW --> WebApp[Web App in Spoke VNet]
FW --> SpokeDB[Other Spokes / Data Tiers]
Real-World Example
Inbound web traffic:
- Users access public web apps hosted in Spoke VNets.
- Azure Firewall Premium inspects TLS traffic, applies IDPS and Threat Intel checks.
- Traffic is then allowed to Application Gateway WAF, which applies OWASP rules to the HTTP/S requests.
- Clean, authorised traffic is then routed to backend VMs or App Service instances.
This setup marries the strength of both services, reducing blind spots.
Implementation Examples
Azure Portal Quick Steps
- Deploy Azure Firewall Premium in Hub VNet.
- Deploy Application Gateway (with WAF v2) into its own subnet, either in Hub or a Spoke.
- Configure Firewall UDRs to steer inbound HTTP/S traffic via Firewall → AppGW → WebApp.
- Enable WAF in Prevention mode with the latest OWASP ruleset.
- Set App Gateway Listener with custom domain + SSL cert.
Bicep Example — App Gateway WAF v2 Skeleton
To continue on our previous post where we split things into modules we have created a few new resources in order to enable this feature set.
📂 Folder Structure (recommended)
1
2
3
4
5
6
7
8
9
10
11
|
/bicep
├─ main.bicep # Entry point
├─ modules/
| ├─ azappgateway.bicep # Azure Application Gateway
| ├─ azfw.bicep # Azure Firewall
| ├─ azfwpolicy.bicep # Azure Firewall Policy
| ├─ azfwpolicypremium.bicep # Azure Firewall Premium Policy
| ├─ keyvault.bicep # Azure KeyVault + Managed Identity (Required for TLS Inspection)
│ ├─ hub.bicep # Hub VNet + Firewall Subnet
│ ├─ spoke.bicep # Spoke VNet + NSG + UDR
│ └─ peering.bicep # Hub-Spoke peering
|
🔹 main.bicep
— Root deployment updated to include additional resource changes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
// Key Vault needs another new Certificate for the Application Gateway
module keyVault './modules/keyvault.bicep' = {
name: 'keyVaultDeployment'
params: {
location: location
certName: 'AzFwTlsCert'
certSubject: 'CN=AzFwTlsCert.brewedinthecloud.com'
keyVaultName: 'kv-azfw-hub'
appgwcertName: 'AppGwTlsCert' //I'm using self signed in the example
}
}
// Deploy Azure Application Gateway
module appGateway './modules/azappgw.bicep' = {
name: 'appGatewayDeployment'
params: {
location: location
subnetNameid: hub.outputs.appgwSubnetId
appGwName: 'appgw-hub'
appGwPublicIPName: 'appgw-hub-publicIP'
keyVaultId: keyVault.outputs.keyVaultId
appGwCertName: keyVault.outputs.appgwcertName
}
}
|
🔹 modules/hub.bicep
— Our secure hub virtual network needs an additional subnet for the Application Gateway
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
resource hubVnet 'Microsoft.Network/virtualNetworks@2023-05-01' = {
name: hubVnetName
location: location
properties: {
addressSpace: {
addressPrefixes: [hubAddressPrefix]
}
subnets: [
{
name: 'AzureFirewallSubnet'
properties: { addressPrefix: '10.0.1.0/24' }
}
{
name: 'GatewaySubnet'
properties: { addressPrefix: '10.0.255.0/27' }
}
{
name: 'SharedServices'
properties: { addressPrefix: '10.0.2.0/24' }
}
{
name: 'ApplicationGateway'
properties: { addressPrefix: '10.0.3.0/24' }
}
]
}
}
output appgwSubnetId string = hubVnet.properties.subnets[3].id
|
🔹 modules/azappgateway.bicep
— Deploy Azure Application Gateway with WAFv2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
param location string
param subnetNameid string
param appGwName string
param appGwPublicIPName string
param appGwCertName string
param keyVaultId string
resource appGwPublicIP 'Microsoft.Network/publicIPAddresses@2023-05-01' = {
name: appGwPublicIPName
location: location
sku: {
name: 'Standard'
tier: 'Regional'
}
}
resource appgwwafpolicy 'Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies@2024-07-01' = {
name: '${appGwName}-wafpolicy'
location: location
properties: {
policySettings: {
enabledState: 'Enabled'
mode: 'Prevention'
requestBodyCheck: true
maxRequestBodySizeInKb: 128
fileUploadLimitInMb: 100
exclusions: []
}
customRules: []
managedRules: {
managedRuleSets: [
{
ruleSetType: 'OWASP'
ruleSetVersion: '3.2'
ruleGroupOverrides: []
}
]
}
}
}
resource appGw 'Microsoft.Network/applicationGateways@2023-05-01' = {
name: appGwName
location: location
sku: {
name: 'WAF_v2'
tier: 'WAF_v2'
capacity: 2
}
properties: {
autoscaleConfiguration: {
minCapacity: 2
}
gatewayIPConfigurations: [
{
name: 'appGwIpConfig'
properties: {
subnet: {
id: subnetNameid
}
}
}
]
frontendIPConfigurations: [
{
name: 'appGwFrontendIP-port443'
properties: {
publicIPAddress: {
id: appGwPublicIP.id
}
}
}
]
frontendPorts: [
{
name: 'appGwFrontendPort-443'
properties: {
port: 443
}
}
]
backendAddressPools: [
{
name: 'appGwBackendPool'
properties: {
backendAddresses: [
]
}
}
]
backendHttpSettingsCollection: [
{
name: 'appGwBackendHttpSettings'
properties: {
port: 80
protocol: 'Http'
cookieBasedAffinity: 'Disabled'
pickHostNameFromBackendAddress: false
requestTimeout: 20
}
}
]
httpListeners: [
{
name: 'appGwHttpsListener'
properties: {
frontendIPConfiguration: {
id: resourceId('Microsoft.Network/applicationGateways/frontendIPConfigurations', appGwName, 'appGwFrontendIP')
}
frontendPort: {
id: resourceId('Microsoft.Network/applicationGateways/frontendPorts', appGwName, 'appGwFrontendPort')
}
protocol: 'Https'
sslCertificate: {
id: '${keyVaultId}/certificates/${appGwCertName}'
}
requireServerNameIndication: false
}
}
]
firewallPolicy: {
id: appgwwafpolicy.id
}
enableHttp2: true
}
}
|
Gotchas & Edge Cases
- App Gateway Placement — Deploy in its own subnet; don’t cram it into shared services.
- Chaining Adds Latency — More security checks = more hops. Test carefully for user‑facing apps.
- Certificates — Both Firewall TLS inspection and App Gateway TLS termination need managed cert lifecycle.
- Costs — Running WAF + Firewall Premium together isn’t cheap. Justify with compliance or risk posture.
Best Practices
- Place App Gateway close to app workloads (same region, minimal latency).
- Use WAF_v2 or higher for autoscaling and zone redundancy.
- Only chain Firewall + App Gateway when you need both network + app layer security on inbound flows.
- Feed WAF logs into Log Analytics to correlate alerts with Firewall.
🍺
Brewed Insight: App Gateway WAF is not a general-purpose firewall. It’s a purpose-built HTTP/S filter for protecting web apps, and it plays best when paired with Azure Firewall. Call it the hops to your malt — neither alone makes a great beer, but together they give it balance.
Learn More