Azure Application Gateway can route requests based on URL path. Route /api/v1/* to one backend, /api/v2/* to another, and /static/* to blob storage.
Use Cases
- API Versioning - Route old and new API versions to different backends
- Microservices - Route
/users/*to user service,/orders/*to order service - Migrations - Gradually move paths to new infrastructure
- Static Content - Route static assets to cheaper storage
Basic Path Routing
resource "azurerm_application_gateway" "this" {
name = "appgw-production"
resource_group_name = azurerm_resource_group.this.name
location = azurerm_resource_group.this.location
# ... SKU, gateway IP config, frontend config ...
backend_address_pool {
name = "api-v1-pool"
fqdns = ["api-v1.internal.company.com"]
}
backend_address_pool {
name = "api-v2-pool"
fqdns = ["api-v2.internal.company.com"]
}
backend_address_pool {
name = "static-pool"
fqdns = ["staticontent.blob.core.windows.net"]
}
# URL path map
url_path_map {
name = "path-routing"
default_backend_address_pool_name = "api-v2-pool"
default_backend_http_settings_name = "api-settings"
path_rule {
name = "v1-api"
paths = ["/api/v1/*"]
backend_address_pool_name = "api-v1-pool"
backend_http_settings_name = "api-settings"
}
path_rule {
name = "v2-api"
paths = ["/api/v2/*"]
backend_address_pool_name = "api-v2-pool"
backend_http_settings_name = "api-settings"
}
path_rule {
name = "static"
paths = ["/static/*", "/images/*", "/css/*", "/js/*"]
backend_address_pool_name = "static-pool"
backend_http_settings_name = "static-settings"
}
}
request_routing_rule {
name = "main-rule"
rule_type = "PathBasedRouting"
http_listener_name = "https-listener"
url_path_map_name = "path-routing"
priority = 100
}
}
Redirect Rules for API Deprecation
Redirect old API paths to new versions:
redirect_configuration {
name = "v1-to-v2-redirect"
redirect_type = "Permanent" # 301
target_url = "https://api.company.com/api/v2/"
include_path = false
include_query_string = true
}
url_path_map {
name = "path-routing"
default_backend_address_pool_name = "api-v2-pool"
default_backend_http_settings_name = "api-settings"
path_rule {
name = "v1-redirect"
paths = ["/api/v1/*"]
redirect_configuration_name = "v1-to-v2-redirect"
}
}
Blackhole Traffic
Block access to certain paths entirely:
# Create an empty backend pool - requests will fail
backend_address_pool {
name = "blackhole"
# No addresses - requests will get 502
}
path_rule {
name = "block-admin"
paths = ["/admin/*", "/wp-admin/*", "/phpmyadmin/*"]
backend_address_pool_name = "blackhole"
backend_http_settings_name = "api-settings"
}
Or use rewrite rules to return a specific response.
Rewrite Rules
Modify requests before they reach the backend:
rewrite_rule_set {
name = "api-rewrites"
rewrite_rule {
name = "strip-version-prefix"
rule_sequence = 100
condition {
variable = "var_uri_path"
pattern = "^/api/v[0-9]+/(.*)"
ignore_case = true
negate = false
}
url {
path = "/api/{var_uri_path_1}"
query_string = null
}
}
}
# Apply to path rule
path_rule {
name = "v2-api"
paths = ["/api/v2/*"]
backend_address_pool_name = "api-v2-pool"
backend_http_settings_name = "api-settings"
rewrite_rule_set_name = "api-rewrites"
}
Path Priority
When paths overlap, more specific paths win:
path_rule {
name = "specific-users"
paths = ["/api/v2/users/admin/*"]
# ... routes to admin backend
}
path_rule {
name = "general-users"
paths = ["/api/v2/users/*"]
# ... routes to user service
}
Request to /api/v2/users/admin/settings matches the first rule.
Health Probes Per Backend
Each backend can have custom health probes:
probe {
name = "api-v1-probe"
protocol = "Https"
path = "/health"
host = "api-v1.internal.company.com"
interval = 30
timeout = 10
unhealthy_threshold = 3
}
probe {
name = "api-v2-probe"
protocol = "Https"
path = "/api/v2/health"
host = "api-v2.internal.company.com"
interval = 30
timeout = 10
unhealthy_threshold = 3
}
backend_http_settings {
name = "api-v1-settings"
cookie_based_affinity = "Disabled"
port = 443
protocol = "Https"
probe_name = "api-v1-probe"
}
Monitoring Path Performance
KQL query to compare latency by path:
AzureDiagnostics
| where ResourceType == "APPLICATIONGATEWAYS"
| where TimeGenerated > ago(1h)
| extend path = tostring(split(requestUri_s, "?")[0])
| summarize
AvgLatency = avg(timeTaken_d),
P95Latency = percentile(timeTaken_d, 95),
RequestCount = count()
by path
| order by RequestCount desc
| take 20
Need help architecting your API gateway? Get in touch - we help organisations design scalable and secure web architectures.