Skip to content

ALB listener rules — host and path routing at scale

Host-based for multi-tenant subdomains, path-based for SPA + API, weighted target groups for canary, fixed-response 503 for maintenance. And the 100-rule cap that blocks you at customer 90.

A multi-tenant SaaS launches with acme.app.example.com, globex.app.example.com, and a wildcard ACM certificate. The first 30 customers get a one-line ALB listener rule each, routing their subdomain to the same target group. At customer 90 the deployment fails — TooManyRulesException. The team learns that day that ALB caps at 100 rules per listener by default, and the rule-per-tenant pattern has a runway of about 95 tenants. The fix is not “raise the quota” — AWS will raise it, but the underlying cost is that ALB evaluates every rule on every request, and a 500-rule listener is slower and harder to operate than a 50-rule listener. The pattern is to use the rule slots for routing decisions, not for tenant identity, and to push tenant identity into the application or a downstream layer.

What’s happening

ALB listener rules are evaluated in priority order against every incoming request. Each rule has up to five conditions (host, path, header, query string, source IP, HTTP method, HTTP version) and one of a handful of actions: forward to a target group, redirect, return a fixed response, authenticate via Cognito or OIDC, or fix the request and forward.

The default ALB account quota is 100 rules per listener. The hard limit (after a quota increase) is 200 rules per listener. Each rule consumes resources at request time, and the listener becomes harder to reason about as the count grows. The patterns that scale:

  • Host-based routing matches the Host header. Good for multi-tenant SaaS where every tenant has a subdomain but the application is the same — one rule with a wildcard host condition forwards *.app.example.com to the application target group, and the application reads the subdomain to determine the tenant.

  • Path-based routing matches the URL path. Good for a single domain with multiple services behind it — /api/* to one target group, /static/* to a different one, / to the SPA fallback. Path rules are how an SPA + API split lives behind one ALB.

  • Weighted target groups let one rule forward to multiple target groups with different weights — 95% to the stable target group and 5% to the canary. Useful for gradual rollouts without DNS changes.

  • Fixed-response actions return a static HTTP response without forwarding to any target. The classic use is a maintenance-mode 503 page: one rule at priority 1 with the condition header X-Maintenance-Mode = on returning a 503 fixed response, and a runtime toggle that adds the header to all requests when maintenance is on.

  • Redirect actions return a 301 or 302 without forwarding. Good for legacy URL migrations or HTTP-to-HTTPS at the listener level.

The 100-rule cap bites the host-based-routing-per-tenant pattern hardest. Patterns that survive scale:

  1. One wildcard rule, application-level tenant resolution. *.app.example.com is one rule. The application reads the Host header and resolves the tenant. Scales to millions of tenants.

  2. Per-rule for tier or region. One rule per category (enterprise vs standard, US vs EU), and the application handles per-tenant routing within the category. Stays under 20 rules total.

  3. Two ALBs per region. Some workloads warrant one ALB for the customer-facing tenants and a second for internal admin or partner traffic. Two listeners, two budgets of 100 rules each.

  4. CloudFront in front of ALB. CloudFront can route by host or path to different origins, and CloudFront limits are higher (or you can use Lambda@Edge for arbitrary logic). The pattern: CloudFront handles the per-tenant or per-region routing decision, then forwards to one of a small number of ALBs.

How to verify

Listing the current rules on a listener:

LISTENER_ARN=$(aws elbv2 describe-listeners \
  --load-balancer-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/prod-alb/abc \
  --query 'Listeners[?Port==`443`].ListenerArn' --output text)

aws elbv2 describe-rules --listener-arn "$LISTENER_ARN" \
  --query 'Rules[].{Priority:Priority,Conditions:Conditions[].Field,Actions:Actions[].Type}' \
  --output table

Counting how many rules a listener uses, useful in an alarm:

aws elbv2 describe-rules --listener-arn "$LISTENER_ARN" \
  --query 'length(Rules)'

A CloudWatch alarm on ActiveConnectionCount per rule is not native, but RuleEvaluations is available — if one rule sees orders of magnitude more traffic than the others, you have a hot rule that may warrant its own listener or its own target group with more capacity.

For host-based wildcard rules, the wildcard is at the leftmost label only. *.app.example.com matches acme.app.example.com but not app.example.com (you need a separate rule for the bare domain) and not deep.acme.app.example.com. Confirm against the actual Host header the client is sending.

Adding a canary at 5% weight:

aws elbv2 modify-rule \
  --rule-arn "$RULE_ARN" \
  --actions '[{
    "Type": "forward",
    "ForwardConfig": {
      "TargetGroups": [
        {"TargetGroupArn": "arn:aws:...stable", "Weight": 95},
        {"TargetGroupArn": "arn:aws:...canary", "Weight": 5}
      ]
    }
  }]'

Verify the weighted forward returned the right config:

aws elbv2 describe-rules --rule-arns "$RULE_ARN" \
  --query 'Rules[].Actions[].ForwardConfig.TargetGroups'

The maintenance-mode pattern, deployed once:

# Rule at priority 1, evaluated first, returning 503 when the header is set
aws elbv2 create-rule \
  --listener-arn "$LISTENER_ARN" \
  --priority 1 \
  --conditions '[{"Field":"http-header","HttpHeaderConfig":{"HttpHeaderName":"X-Maintenance-Mode","Values":["on"]}}]' \
  --actions '[{
    "Type": "fixed-response",
    "FixedResponseConfig": {
      "ContentType": "text/html",
      "StatusCode": "503",
      "MessageBody": "<html><body><h1>Maintenance in progress</h1></body></html>"
    }
  }]'

Then the maintenance window is a single header flip at a CDN or WAF layer, not an ALB rule change in the hot path.

The fix

The runbook for a listener approaching the rule cap:

  1. Audit the existing rules. Print them with their condition counts. Rules that match host = single-customer.app.example.com are the candidate for collapse.

  2. Identify the routing dimensions. If every per-tenant rule forwards to the same target group, the rule is pure tenant identity and belongs in the application. If different tenants forward to different target groups (premium vs standard, US vs EU), the dimension is tier or region — those are the rules worth keeping.

  3. Collapse host rules into wildcards by dimension. *.enterprise.app.example.com to the enterprise target group, *.app.example.com to the standard target group, in that priority order. Tenant identity flows through the Host header into the application.

  4. Push per-tenant logic into the application. The application reads Host, looks up the tenant in a database, and applies tenant-specific behavior. The ALB is no longer the place where tenants are defined.

  5. Cap the listener rule count via process, not just quota. A pull-request check that fails if the rule count exceeds 50 forces the team to think about whether each new rule is necessary. The quota is the ceiling; the budget is lower.

Three guardrails worth holding:

Priority gaps. Number rules in increments of 10 (10, 20, 30) so inserts later do not require renumbering everything. ALB does not auto-renumber.

Default action discipline. The listener’s default action is the fallback when no rule matches. Set it to a 404 fixed response or a target group that returns a generic 404, not to the main application. A misconfigured rule that fails to match should not silently route to production.

Fixed-response over redirect. A redirect is a round-trip and a security risk if the target is parameterized. A fixed response is one packet and cannot be redirected somewhere else by a crafted query string.

For canary deployments behind ALB and the related health-check tuning, see Health check tuning for SaaS targets. For the ALB vs NLB decision when partner integrations need static IPs, see ALB vs NLB vs GWLB.


Stack Harbor designs ALB + NLB topologies and the operating model around them as part of Managed AWS onboarding and ongoing Managed Cloud Ops. If your listener has accreted one rule per tenant, start with an infrastructure consult.