Application Gateway with WAF is the single most important piece of infrastructure you’ll deploy for web application security in Azure, and it’s not because it blocks attacks. It’s because it’s the only place you can see the attacks.

Let’s imagine you’ve got a web app running on a VM scale set behind an Application Gateway. You’ve enabled the Web Application Firewall (WAF) because, well, you should.

Here’s a snippet of a typical Application Gateway configuration in ARM or Bicep, showing the WAF policy attached:

{
  "type": "Microsoft.Network/applicationGateways",
  "apiVersion": "2023-06-01",
  "name": "my-app-gateway",
  "location": "eastus",
  "properties": {
    "sku": {
      "name": "WAF_v2",
      "tier": "WAF_v2"
    },
    "gatewayIPConfigurations": [
      {
        "name": "appGatewayIpConfig",
        "properties": {
          "subnet": {
            "id": "/subscriptions/<sub_id>/resourceGroups/<rg_name>/providers/Microsoft.Network/virtualNetworks/<vnet_name>/subnets/AppGatewaySubnet"
          }
        }
      }
    ],
    "frontendIPConfigurations": [
      {
        "name": "publicFrontendIP",
        "properties": {
          "publicIPAddress": {
            "id": "/subscriptions/<sub_id>/resourceGroups/<rg_name>/providers/Microsoft.Network/publicIPAddresses/my-app-gateway-pip"
          }
        }
      }
    ],
    "backendAddressPools": [
      {
        "name": "appBackendPool",
        "properties": {
          "backendAddresses": [
            {
              "ipAddress": "10.0.1.4"
            },
            {
              "ipAddress": "10.0.1.5"
            }
          ]
        }
      }
    ],
    "httpListeners": [
      {
        "name": "httpListener",
        "properties": {
          "frontendIPConfiguration": {
            "id": "/subscriptions/<sub_id>/resourceGroups/<rg_name>/providers/Microsoft.Network/applicationGateways/my-app-gateway/frontendIPConfigurations/publicFrontendIP"
          },
          "frontendPort": {
            "id": "/subscriptions/<sub_id>/resourceGroups/<rg_name>/providers/Microsoft.Network/applicationGateways/my-app-gateway/frontendPorts/port80"
          },
          "protocol": "Http",
          "hostName": "www.example.com"
        }
      }
    ],
    "ports": [
      {
        "name": "port80",
        "properties": {
          "portNumber": 80,
          "protocol": "Http"
        }
      }
    ],
    "requestRoutingRules": [
      {
        "name": "defaultRoutingRule",
        "properties": {
          "priority": 1000,
          "backendAddressPool": {
            "id": "/subscriptions/<sub_id>/resourceGroups/<rg_name>/providers/Microsoft.Network/applicationGateways/my-app-gateway/backendAddressPools/appBackendPool"
          },
          "httpListener": {
            "id": "/subscriptions/<sub_id>/resourceGroups/<rg_name>/providers/Microsoft.Network/applicationGateways/my-app-gateway/httpListeners/httpListener"
          },
          "backendHttpSettings": {
            "id": "/subscriptions/<sub_id>/resourceGroups/<rg_name>/providers/Microsoft.Network/applicationGateways/my-app-gateway/backendHttpSettingsCollection/defaultBackendSettings"
          }
        }
      }
    ],
    "backendHttpSettingsCollection": [
      {
        "name": "defaultBackendSettings",
        "properties": {
          "port": 80,
          "protocol": "Http",
          "cookieBasedAffinity": "Disabled",
          "pickHostnameFromBackendAddress": false
        }
      }
    ],
    "webApplicationFirewallConfiguration": {
      "enabled": true,
      "firewallMode": "Prevention",
      "ruleSetType": "OWASP",
      "ruleSetVersion": "3.2",
      "managedRuleSetIds": [
        "/subscriptions/<sub_id>/resourceGroups/<rg_name>/providers/Microsoft.Network/ApplicationGatewayWebApplicationFirewallSettings/DefaultManagedRuleSet"
      ]
    }
  }
}

The WAF, when in Prevention mode, intercepts incoming requests to your application. It inspects them against a set of predefined rules (like the OWASP Core Rule Set) and blocks any that match malicious patterns. This is crucial for protecting against common web exploits like SQL injection, cross-site scripting (XSS), and more.

But the real power isn’t just blocking. It’s the visibility. When the WAF blocks a request, it logs it. This is where you see the attack attempts, not just the successful breaches. You can configure diagnostic settings for your Application Gateway to send these logs to Log Analytics, Storage Accounts, or Event Hubs.

Setting it up involves a few key pieces:

  1. Application Gateway SKU: You must choose a WAF SKU. This means WAF_v2 or WAF_v1. WAF_v2 is the current generation and recommended. It offers autoscaling and higher availability.

  2. WAF Policy: This is a separate resource that defines how the WAF operates. You associate a WAF policy with your Application Gateway. This policy contains configurations like:

    • Managed Rule Sets: Which set of rules to use (e.g., OWASP 3.2, 3.1).
    • Custom Rules: Rules you define yourself based on specific conditions.
    • Exclusions: Patterns to ignore so legitimate traffic isn’t blocked.
    • Detection/Prevention Mode: Prevention blocks traffic; Detection only logs it.
    • Bot Protection: Advanced features to identify and manage bot traffic.

    Here’s an example of a WAF Policy:

    {
      "type": "Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies",
      "apiVersion": "2023-06-01",
      "name": "my-waf-policy",
      "location": "eastus",
      "properties": {
        "policySettings": {
          "mode": "Prevention",
          "state": "Enabled",
          "requestBodyInspectLimitInKB": 128,
          "requestBodyEnforcement": true,
          "fileUploadLimitInMB": 100,
          "maxAllowedCookieValueLength": 4096,
          "maxRequestHeaderFields": 100,
          "redirectUrl": "https://www.example.com/blocked",
          "customBlockResponseStatusCode": 403
        },
        "managedRuleSets": [
          {
            "ruleSetType": "OWASP",
            "ruleSetVersion": "3.2",
            "ruleGroupOverrides": []
          }
        ],
        "customRules": [],
        "exclusionManagedRuleSets": []
      }
    }
    
  3. Association: You link the WAF Policy to your Application Gateway in the webApplicationFirewallConfiguration section of the Application Gateway’s properties, as shown in the first ARM snippet.

When you enable the WAF, it begins inspecting all traffic hitting your Application Gateway. If a request violates a rule (e.g., contains ../ in a URL parameter, indicating a potential directory traversal attack), the WAF takes action. In Prevention mode, it drops the request and returns a 403 Forbidden response (or a custom response if configured). In Detection mode, it simply logs the event without blocking.

The most overlooked aspect of WAF configuration is tuning. The default OWASP rules are robust, but they can be overly aggressive for certain applications. You’ll find yourself needing to create exclusions for legitimate traffic that triggers false positives. For example, if your application uses a specific query parameter that happens to look like a SQL injection attempt, you’d add an exclusion for that parameter.

A common mistake is not enabling diagnostic logging for the WAF. Without logs, you’re flying blind. You won’t see the attacks that are being blocked, nor will you have the data to tune your exclusions. Ensure you are sending ApplicationGatewayFirewallLog to a Log Analytics workspace.

The next thing you’ll need to wrestle with is custom rules and rate limiting.

Want structured learning?

Take the full Azure course →