← Back to Guides

Application Gateway and Web Application Firewall

IntermediateAzure Networking2026-03-14

What Is Azure Application Gateway?

Azure Application Gateway is a Layer 7 (HTTP/HTTPS) load balancer and application delivery controller. Unlike Traffic Manager (DNS-level), Application Gateway sits in the data path and can inspect, route, and modify HTTP traffic.

Official Documentation: Application Gateway overview

Application Gateway vs Traffic Manager

Application Gateway

Layer 7 HTTP/HTTPS load balancer
LayerLayer 7 (HTTP/HTTPS)
In data pathYes — proxies all requests
ScopeRegional (single region)
SSL terminationYes
URL-based routingYes
WAFYes (built-in)
Session affinityYes (cookie-based)
WebSocket supportYes
Health probesHTTP/HTTPS with body inspection
🌐

Traffic Manager

Global DNS-level traffic routing
LayerDNS-level
In data pathNo — DNS resolution only
ScopeGlobal (cross-region)
SSL terminationNo
URL-based routingNo
WAFNo
Session affinityNo
WebSocket supportNo
Health probesHTTP/HTTPS/TCP status code

Common pattern: Use Traffic Manager for global DNS-based routing across regions, with Application Gateway in each region for Layer 7 load balancing and WAF.

Architecture

Internet
    ↓
Application Gateway (with WAF)
    ↓ URL-based routing
    ↓ SSL termination
    ↓ Header rewriting
    ↓
┌─────────────────────────────────────────┐
│  Backend Pool A        Backend Pool B   │
│  /api/* → APIM         /web/* → App Svc │
│  (api-prod.azure.com)  (web-prod.azure) │
└─────────────────────────────────────────┘

SKU Tiers

SKU Features Use Case
Standard v2 Autoscaling, zone redundancy, static VIP, header rewrite Production workloads without WAF
WAF v2 All Standard v2 features + Web Application Firewall Production workloads requiring WAF

Note: V1 SKUs (Standard and WAF) are deprecated. Always use v2.

Core Components

Component Description
Frontend IP Public and/or private IP address receiving traffic
Listener Receives incoming connections on a port and protocol
Rule Maps a listener to a backend pool via routing rules
Backend Pool Group of backend targets (VMs, VMSS, App Services, IPs)
HTTP Settings Backend connection config (port, protocol, timeouts, affinity)
Health Probe Monitors backend health with HTTP/HTTPS requests
URL Path Map Routes requests to different backends based on URL path
Rewrite Rules Modify request/response headers and URL

Deploying Application Gateway

Bicep Deployment

param location string = resourceGroup().location
param environment string

// Virtual Network and Subnet (required)
resource vnet 'Microsoft.Network/virtualNetworks@2023-09-01' = {
  name: 'vnet-appgw-${environment}'
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [ '10.0.0.0/16' ]
    }
    subnets: [
      {
        name: 'snet-appgw'
        properties: {
          addressPrefix: '10.0.1.0/24'   // Dedicated subnet for App Gateway
        }
      }
      {
        name: 'snet-backend'
        properties: {
          addressPrefix: '10.0.2.0/24'
        }
      }
    ]
  }
}

// Public IP
resource publicIp 'Microsoft.Network/publicIPAddresses@2023-09-01' = {
  name: 'pip-appgw-${environment}'
  location: location
  sku: {
    name: 'Standard'
  }
  properties: {
    publicIPAllocationMethod: 'Static'
  }
}

// Application Gateway with WAF
resource appGateway 'Microsoft.Network/applicationGateways@2023-09-01' = {
  name: 'agw-integration-${environment}'
  location: location
  properties: {
    sku: {
      name: 'WAF_v2'
      tier: 'WAF_v2'
    }
    autoscaleConfiguration: {
      minCapacity: 2
      maxCapacity: 10
    }
    gatewayIPConfigurations: [
      {
        name: 'appGatewayIpConfig'
        properties: {
          subnet: {
            id: vnet.properties.subnets[0].id
          }
        }
      }
    ]
    frontendIPConfigurations: [
      {
        name: 'appGwPublicFrontendIp'
        properties: {
          publicIPAddress: {
            id: publicIp.id
          }
        }
      }
    ]
    frontendPorts: [
      {
        name: 'port_443'
        properties: { port: 443 }
      }
      {
        name: 'port_80'
        properties: { port: 80 }
      }
    ]
    sslCertificates: [
      {
        name: 'ssl-cert'
        properties: {
          keyVaultSecretId: 'https://kv-certs-prod.vault.azure.net/secrets/wildcard-cert'
        }
      }
    ]
    httpListeners: [
      {
        name: 'listener-https'
        properties: {
          frontendIPConfiguration: {
            id: resourceId('Microsoft.Network/applicationGateways/frontendIPConfigurations', 'agw-integration-${environment}', 'appGwPublicFrontendIp')
          }
          frontendPort: {
            id: resourceId('Microsoft.Network/applicationGateways/frontendPorts', 'agw-integration-${environment}', 'port_443')
          }
          protocol: 'Https'
          sslCertificate: {
            id: resourceId('Microsoft.Network/applicationGateways/sslCertificates', 'agw-integration-${environment}', 'ssl-cert')
          }
        }
      }
      {
        name: 'listener-http-redirect'
        properties: {
          frontendIPConfiguration: {
            id: resourceId('Microsoft.Network/applicationGateways/frontendIPConfigurations', 'agw-integration-${environment}', 'appGwPublicFrontendIp')
          }
          frontendPort: {
            id: resourceId('Microsoft.Network/applicationGateways/frontendPorts', 'agw-integration-${environment}', 'port_80')
          }
          protocol: 'Http'
        }
      }
    ]
    backendAddressPools: [
      {
        name: 'pool-api'
        properties: {
          backendAddresses: [
            { fqdn: 'apim-prod.azure-api.net' }
          ]
        }
      }
      {
        name: 'pool-webapp'
        properties: {
          backendAddresses: [
            { fqdn: 'app-web-prod.azurewebsites.net' }
          ]
        }
      }
    ]
    backendHttpSettingsCollection: [
      {
        name: 'settings-api'
        properties: {
          port: 443
          protocol: 'Https'
          cookieBasedAffinity: 'Disabled'
          requestTimeout: 30
          pickHostNameFromBackendAddress: true
          probe: {
            id: resourceId('Microsoft.Network/applicationGateways/probes', 'agw-integration-${environment}', 'probe-api')
          }
        }
      }
      {
        name: 'settings-webapp'
        properties: {
          port: 443
          protocol: 'Https'
          cookieBasedAffinity: 'Enabled'
          requestTimeout: 60
          pickHostNameFromBackendAddress: true
          probe: {
            id: resourceId('Microsoft.Network/applicationGateways/probes', 'agw-integration-${environment}', 'probe-webapp')
          }
        }
      }
    ]
    probes: [
      {
        name: 'probe-api'
        properties: {
          protocol: 'Https'
          path: '/health'
          interval: 30
          timeout: 10
          unhealthyThreshold: 3
          pickHostNameFromBackendHttpSettings: true
          match: {
            statusCodes: [ '200-399' ]
          }
        }
      }
      {
        name: 'probe-webapp'
        properties: {
          protocol: 'Https'
          path: '/'
          interval: 30
          timeout: 10
          unhealthyThreshold: 3
          pickHostNameFromBackendHttpSettings: true
          match: {
            statusCodes: [ '200-399' ]
          }
        }
      }
    ]
    urlPathMaps: [
      {
        name: 'url-path-map'
        properties: {
          defaultBackendAddressPool: {
            id: resourceId('Microsoft.Network/applicationGateways/backendAddressPools', 'agw-integration-${environment}', 'pool-webapp')
          }
          defaultBackendHttpSettings: {
            id: resourceId('Microsoft.Network/applicationGateways/backendHttpSettingsCollection', 'agw-integration-${environment}', 'settings-webapp')
          }
          pathRules: [
            {
              name: 'api-path'
              properties: {
                paths: [ '/api/*' ]
                backendAddressPool: {
                  id: resourceId('Microsoft.Network/applicationGateways/backendAddressPools', 'agw-integration-${environment}', 'pool-api')
                }
                backendHttpSettings: {
                  id: resourceId('Microsoft.Network/applicationGateways/backendHttpSettingsCollection', 'agw-integration-${environment}', 'settings-api')
                }
              }
            }
          ]
        }
      }
    ]
    requestRoutingRules: [
      {
        name: 'rule-https'
        properties: {
          priority: 100
          ruleType: 'PathBasedRouting'
          httpListener: {
            id: resourceId('Microsoft.Network/applicationGateways/httpListeners', 'agw-integration-${environment}', 'listener-https')
          }
          urlPathMap: {
            id: resourceId('Microsoft.Network/applicationGateways/urlPathMaps', 'agw-integration-${environment}', 'url-path-map')
          }
        }
      }
      {
        name: 'rule-http-redirect'
        properties: {
          priority: 200
          ruleType: 'Basic'
          httpListener: {
            id: resourceId('Microsoft.Network/applicationGateways/httpListeners', 'agw-integration-${environment}', 'listener-http-redirect')
          }
          redirectConfiguration: {
            id: resourceId('Microsoft.Network/applicationGateways/redirectConfigurations', 'agw-integration-${environment}', 'redirect-http-to-https')
          }
        }
      }
    ]
    redirectConfigurations: [
      {
        name: 'redirect-http-to-https'
        properties: {
          redirectType: 'Permanent'
          targetListener: {
            id: resourceId('Microsoft.Network/applicationGateways/httpListeners', 'agw-integration-${environment}', 'listener-https')
          }
          includePath: true
          includeQueryString: true
        }
      }
    ]
    webApplicationFirewallConfiguration: {
      enabled: true
      firewallMode: 'Prevention'
      ruleSetType: 'OWASP'
      ruleSetVersion: '3.2'
      requestBodyCheck: true
      maxRequestBodySizeInKb: 128
      fileUploadLimitInMb: 100
    }
  }
}

SSL Termination

Application Gateway handles SSL/TLS decryption, offloading this work from backend servers:

Client → HTTPS → App Gateway → HTTP or HTTPS → Backend
                (SSL terminated)    (re-encrypted if needed)

SSL Policy

sslPolicy: {
  policyType: 'Predefined'
  policyName: 'AppGwSslPolicy20220101'  // TLS 1.2+ only
}
Policy Min TLS Version Description
AppGwSslPolicy20220101 TLS 1.2 Recommended — strong ciphers only
AppGwSslPolicy20220101S TLS 1.2 Stricter cipher suite
AppGwSslPolicy20170401S TLS 1.2 Legacy strong policy

End-to-End SSL

For end-to-end encryption, configure HTTPS backend settings:

backendHttpSettingsCollection: [
  {
    name: 'settings-e2e-ssl'
    properties: {
      port: 443
      protocol: 'Https'
      trustedRootCertificates: [
        {
          id: resourceId('Microsoft.Network/applicationGateways/trustedRootCertificates', appGwName, 'backend-root-ca')
        }
      ]
    }
  }
]

Web Application Firewall (WAF)

The WAF on Application Gateway protects web applications against common exploits and vulnerabilities defined by OWASP.

WAF Modes

Mode Behaviour
Detection Logs threats but does not block them — use for initial testing
Prevention Blocks requests that match WAF rules — use for production

Recommendation: Start in Detection mode, review logs to tune out false positives, then switch to Prevention.

WAF Rule Sets

Rule Set Description
OWASP 3.2 Core Rule Set — SQL injection, XSS, command injection, etc.
OWASP 3.1 Previous version — still supported
Bot Manager 1.1 Bot detection and mitigation
Custom Rules User-defined rules with priority-based evaluation

What WAF on Application Gateway Protects Against

OWASP Category Examples
SQL Injection ' OR 1=1 --, union-based attacks
Cross-Site Scripting (XSS) <script>alert('xss')</script> in inputs
Command Injection ;cat /etc/passwd in parameters
Local File Inclusion ../../etc/passwd path traversal
Protocol Violations Malformed HTTP headers, request smuggling
Session Fixation Cookie manipulation attacks
Scanner Detection Known vulnerability scanner signatures

WAF Policy (Recommended Approach)

WAF policies provide more granular control than the inline webApplicationFirewallConfiguration:

resource wafPolicy 'Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies@2023-09-01' = {
  name: 'waf-policy-${environment}'
  location: location
  properties: {
    policySettings: {
      state: 'Enabled'
      mode: 'Prevention'
      requestBodyCheck: true
      maxRequestBodySizeInKb: 128
      fileUploadLimitInMb: 100
      requestBodyInspectLimitInKB: 128
    }
    managedRules: {
      managedRuleSets: [
        {
          ruleSetType: 'OWASP'
          ruleSetVersion: '3.2'
          ruleGroupOverrides: [
            {
              ruleGroupName: 'REQUEST-942-APPLICATION-ATTACK-SQLI'
              rules: [
                {
                  ruleId: '942130'
                  state: 'Disabled'    // Disable specific rule causing false positives
                  action: 'AnomalyScoring'
                }
              ]
            }
          ]
        }
        {
          ruleSetType: 'Microsoft_BotManagerRuleSet'
          ruleSetVersion: '1.1'
        }
      ]
      exclusions: [
        {
          matchVariable: 'RequestHeaderNames'
          selectorMatchOperator: 'Equals'
          selector: 'x-api-key'
        }
        {
          matchVariable: 'RequestBodyPostArgNames'
          selectorMatchOperator: 'StartsWith'
          selector: 'config_'
        }
      ]
    }
    customRules: [
      {
        name: 'BlockSpecificIPs'
        priority: 1
        ruleType: 'MatchRule'
        action: 'Block'
        matchConditions: [
          {
            matchVariables: [
              {
                variableName: 'RemoteAddr'
              }
            ]
            operator: 'IPMatch'
            matchValues: [
              '203.0.113.0/24'
              '198.51.100.0/24'
            ]
          }
        ]
      }
      {
        name: 'RateLimitByIP'
        priority: 2
        ruleType: 'RateLimitRule'
        rateLimitDuration: 'OneMin'
        rateLimitThreshold: 100
        action: 'Block'
        matchConditions: [
          {
            matchVariables: [
              {
                variableName: 'RemoteAddr'
              }
            ]
            operator: 'IPMatch'
            negationCondition: true
            matchValues: [ '10.0.0.0/8' ]     // Rate limit external IPs only
          }
        ]
      }
      {
        name: 'AllowHealthProbes'
        priority: 3
        ruleType: 'MatchRule'
        action: 'Allow'
        matchConditions: [
          {
            matchVariables: [
              {
                variableName: 'RequestUri'
              }
            ]
            operator: 'Equal'
            matchValues: [ '/health' ]
          }
        ]
      }
    ]
  }
}

Associating WAF Policy with Application Gateway

resource appGateway 'Microsoft.Network/applicationGateways@2023-09-01' = {
  // ...
  properties: {
    firewallPolicy: {
      id: wafPolicy.id
    }
    // ... rest of configuration
  }
}

Header Rewriting

rewriteRuleSets: [
  {
    name: 'rewrite-rules'
    properties: {
      rewriteRules: [
        {
          name: 'add-security-headers'
          ruleSequence: 100
          actionSet: {
            responseHeaderConfigurations: [
              {
                headerName: 'Strict-Transport-Security'
                headerValue: 'max-age=31536000; includeSubDomains'
              }
              {
                headerName: 'X-Content-Type-Options'
                headerValue: 'nosniff'
              }
              {
                headerName: 'X-Frame-Options'
                headerValue: 'DENY'
              }
            ]
          }
        }
        {
          name: 'forward-client-ip'
          ruleSequence: 200
          actionSet: {
            requestHeaderConfigurations: [
              {
                headerName: 'X-Forwarded-For'
                headerValue: '{var_client_ip}'
              }
              {
                headerName: 'X-Original-Host'
                headerValue: '{var_host}'
              }
            ]
          }
        }
      ]
    }
  }
]

Diagnostic Settings and Monitoring

resource appGwDiagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
  name: 'appgw-diagnostics'
  scope: appGateway
  properties: {
    workspaceId: logAnalyticsWorkspace.id
    logs: [
      { category: 'ApplicationGatewayAccessLog', enabled: true }
      { category: 'ApplicationGatewayPerformanceLog', enabled: true }
      { category: 'ApplicationGatewayFirewallLog', enabled: true }
    ]
    metrics: [
      { category: 'AllMetrics', enabled: true }
    ]
  }
}

KQL: WAF Blocked Requests

AzureDiagnostics
| where ResourceType == "APPLICATIONGATEWAYS"
| where Category == "ApplicationGatewayFirewallLog"
| where action_s == "Blocked"
| summarize BlockedCount = count() by ruleId_s, ruleGroup_s, details_message_s
| order by BlockedCount desc
| take 20

KQL: Access Log Analysis

AzureDiagnostics
| where ResourceType == "APPLICATIONGATEWAYS"
| where Category == "ApplicationGatewayAccessLog"
| where TimeGenerated > ago(1h)
| summarize
    TotalRequests = count(),
    AvgLatency = avg(timeTaken_d),
    P95Latency = percentile(timeTaken_d, 95),
    Errors = countif(httpStatus_d >= 400)
  by bin(TimeGenerated, 5m)
| render timechart

KQL: Backend Health

AzureDiagnostics
| where ResourceType == "APPLICATIONGATEWAYS"
| where Category == "ApplicationGatewayAccessLog"
| summarize
    Total = count(),
    Healthy = countif(httpStatus_d < 500),
    Unhealthy = countif(httpStatus_d >= 500)
  by serverRouted_s
| extend HealthRate = round(100.0 * Healthy / Total, 2)

Naming Conventions

agw-{workload}-{environment}          // Application Gateway
pip-agw-{workload}-{environment}      // Public IP for App Gateway
waf-policy-{environment}              // WAF Policy

Examples:

  • agw-integration-prod / pip-agw-integration-prod
  • waf-policy-prod

Best Practices

  1. Always use WAF v2 SKU — v1 is deprecated and lacks autoscaling
  2. Start WAF in Detection mode and tune before switching to Prevention
  3. Use WAF policies instead of inline configuration for better management
  4. Configure exclusions for known safe request fields to reduce false positives
  5. Enable HTTP to HTTPS redirect to enforce encrypted connections
  6. Use end-to-end SSL for sensitive data — don't stop encryption at the gateway
  7. Set appropriate request timeouts per backend pool (API vs web app)
  8. Enable autoscaling with a minimum of 2 instances for high availability
  9. Deploy in a dedicated subnet — Application Gateway requires its own subnet
  10. Monitor WAF logs in Log Analytics and set alerts on blocked request spikes
  11. Use custom rules for IP blocking, geo-filtering, and rate limiting before managed rules
  12. Combine with Traffic Manager for global multi-region failover

Official Microsoft Resources