Skip to content
AWS aws load-balancing 4 min read

Path- & Host-Based Routing

An Application Load Balancer (ALB — a Layer 7 load balancer that understands HTTP) can do far more than spread traffic evenly. It can read each incoming request and send it to a different backend based on the URL path (like /api/*) or the hostname (like api.example.com). This lets you run many small services behind a single load balancer instead of paying for one load balancer per service. For teams building microservices (an architecture where an app is split into many small, independent services), this is the cheapest and simplest front door you can build.

Why routing rules matter

Without routing rules, an ALB just forwards everything to one default target group (a group of servers, or “targets,” that receive traffic). Routing rules change that. Each rule has a condition (what to match) and an action (where to send it). The ALB checks rules in priority order, top to bottom, and stops at the first match. If nothing matches, the request falls through to the listener’s default action.

The two most common conditions are:

  • Path-based — matches on the URL path, e.g. send /api/* to your backend API and /img/* to an image service.
  • Host-based — matches on the Host header, e.g. send api.example.com to one group and app.example.com to another.

You can also combine them: “host is api.example.com AND path is /v2/*.”

When to use this (and when not to)

ScenarioUse path/host routing?
Several services that share one domainYes — one ALB, multiple rules
Splitting a monolith into microservices graduallyYes — route new paths to new services
You need per-service WAF, certs, or scaling isolationMaybe not — separate ALBs give cleaner isolation
Pure TCP/UDP traffic (no HTTP)No — use a Network Load Balancer instead
Routing on something other than host/path/headerNo — ALB rules only match HTTP attributes

Cost note: A single ALB costs roughly $16–$22/month in base charges (plus LCU usage). Running five separate ALBs instead of one with five rules turns that into ~$80–$110/month for the same traffic. Consolidating services behind one ALB is real money saved.

Path-based routing

Imagine one ALB serving /api/* from an API target group and /img/* from an image target group.

Console steps

  1. Open the EC2 console, go to Load Balancers, select your ALB.
  2. Open the Listeners and rules tab, click the HTTPS:443 (or HTTP:80) listener, then Manage rules.
  3. Click Add rule. Give it a name like api-rule.
  4. Under Add condition, choose Path and enter /api/*. Save the condition.
  5. Under Define action, choose Forward to target groups, pick api-tg. Save.
  6. Set its priority (e.g. 10). Add a second rule for /img/* forwarding to img-tg at priority 20.
  7. Leave the default action to forward to a fallback group (e.g. a “not found” service).

CLI equivalent

aws elbv2 create-rule \
  --listener-arn arn:aws:elasticloadbalancing:us-east-1:111122223333:listener/app/my-alb/0a1b2c3d/abcd1234 \
  --priority 10 \
  --conditions '[{"Field":"path-pattern","PathPatternConfig":{"Values":["/api/*"]}}]' \
  --actions '[{"Type":"forward","TargetGroupArn":"arn:aws:elasticloadbalancing:us-east-1:111122223333:targetgroup/api-tg/0a1b2c3d"}]'

Output:

{
    "Rules": [
        {
            "RuleArn": "arn:aws:elasticloadbalancing:us-east-1:111122223333:listener-rule/app/my-alb/0a1b2c3d/abcd1234/9f8e7d6c",
            "Priority": "10",
            "Conditions": [
                {
                    "Field": "path-pattern",
                    "PathPatternConfig": { "Values": ["/api/*"] }
                }
            ],
            "Actions": [
                { "Type": "forward", "TargetGroupArn": "...targetgroup/api-tg/0a1b2c3d", "Order": 1 }
            ],
            "IsDefault": false
        }
    ]
}

Host-based routing

Same ALB, but now api.example.com and app.example.com point at the same load balancer (via DNS), and the rule splits them.

aws elbv2 create-rule \
  --listener-arn arn:aws:elasticloadbalancing:us-east-1:111122223333:listener/app/my-alb/0a1b2c3d/abcd1234 \
  --priority 30 \
  --conditions '[{"Field":"host-header","HostHeaderConfig":{"Values":["api.example.com"]}}]' \
  --actions '[{"Type":"forward","TargetGroupArn":"arn:aws:elasticloadbalancing:us-east-1:111122223333:targetgroup/api-tg/0a1b2c3d"}]'

In the console it’s identical to the path steps above, except in step 4 you choose Host header as the condition and enter api.example.com.

The gotcha: literal, case-sensitive matching and rule order

This is where most people get tripped up.

  • Patterns match literally. The path /api matches only the exact request to /api. The pattern /api/* matches /api/users and /api/, but not the bare /api. If you want both, add two values: ["/api", "/api/*"].
  • Matching is case-sensitive. /API/orders will not match /api/*. Normalize your routes or add explicit patterns.
  • Order is everything. Rules are evaluated by ascending priority number, and the first match wins. If a broad rule /* sits at priority 5 and a specific /api/* rule sits at priority 10, the broad rule grabs everything first and /api/* never fires. Put specific rules before general ones.
  • The only wildcards allowed are * (any number of characters) and ? (exactly one character).

Warning: A common production bug is putting a catch-all /* rule at a low priority number. It silently swallows traffic meant for more specific rules. Always give catch-alls the highest priority number (lowest precedence), or just use the listener’s default action for the catch-all.

IaC example (CloudFormation)

ApiRule:
  Type: AWS::ElasticLoadBalancingV2::ListenerRule
  Properties:
    ListenerArn: !Ref HttpsListener
    Priority: 10
    Conditions:
      - Field: path-pattern
        PathPatternConfig:
          Values:
            - /api
            - /api/*
    Actions:
      - Type: forward
        TargetGroupArn: !Ref ApiTargetGroup

Best practices

  • List specific paths (/api/*) at lower priority numbers than broad catch-alls (/*).
  • Add both /api and /api/* when you want the bare path to route too.
  • Keep paths lowercase by convention, since matching is case-sensitive.
  • Use the listener’s default action for your fallback instead of a low-precedence /* rule.
  • Combine host + path conditions to version APIs (e.g. api.example.com + /v2/*).
  • An ALB allows up to 100 rules per listener — plenty for consolidating many services, but plan groupings if you approach the limit.
  • Prefer one consolidated ALB for cost, but split into separate ALBs when services need isolated certificates, WAF policies, or independent scaling.
Last updated June 15, 2026
Was this helpful?