Loading
Reza Chegini

Junior DevOps Engineer

Junior Cloud Engineer

Junior Site Reliability Engineer

Software Engineer

Backend Developer

Reza Chegini

Junior DevOps Engineer

Junior Cloud Engineer

Junior Site Reliability Engineer

Software Engineer

Backend Developer

Blog Post

Host-Based Routing with AWS ALB and Terraform – Secure Multi-App Access by Domain

June 2, 2025 AWS, DevOps, Infrastructure, Terraform
Host-Based Routing with AWS ALB and Terraform – Secure Multi-App Access by Domain

In Part 4, we configured Path-Based Routing using Terraform to forward traffic based on URL paths like /app1 and /app2.

Now in Part 5, we enhance that setup by switching to Host Header-Based Routing, which allows you to route requests based on subdomains like:

  • app1.rezaops.com → App 1 backend
  • app2.rezaops.com → App 2 backend

This method is ideal for hosting multiple services or environments behind a single ALB using clean, production-style URLs.


🔁 What Did We Change From the Previous Setup?

We’re still using the same ALB module, but we updated the HTTPS listener rules. Instead of checking for paths, we now check for host headers.

Here’s the updated configuration, with key differences explained.


🔧 ALB Module Overview (No Structural Changes)

module "alb" {
source = "terraform-aws-modules/alb/aws"
version = "9.11.0"

name = "${local.name}-alb"
load_balancer_type = "application"
vpc_id = module.vpc.vpc_id
subnets = module.vpc.public_subnets
security_groups = [module.loadbalancer_sg.security_group_id]
enable_deletion_protection = false

Same as before – we’re deploying an ALB in public subnets with HTTP and HTTPS access.


🔒 HTTP to HTTPS Redirect (Still Present)

my-http-https-redirect = {
port = 80
protocol = "HTTP"
redirect = {
port = "443"
protocol = "HTTPS"
status_code = "HTTP_301"
}
}

✅ Ensures all HTTP requests are redirected to HTTPS — no change here.


🧭 Updated HTTPS Listener – Host Header Based Routing

🔁 Before (Part 3):

path_pattern = {
values = ["/app1*"]
}

🔄 Now (Part 4):

We use host_header instead of path patterns:

my-https-listener = {
port = 443
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-TLS13-1-2-Res-2021-06"
certificate_arn = module.acm.acm_certificate_arn

fixed_response = {
content_type = "text/plain"
message_body = "Fixed Static message - for Root Context"
status_code = "200"
}

rules = {
myapp1-rule = {
actions = [{
type = "weighted-forward"
target_groups = [{ target_group_key = "mytg1", weight = 1 }]
stickiness = { enabled = true, duration = 3600 }
}]
conditions = [{
host_header = {
values = [var.app1_dns_name]
}
}]
},

myapp2-rule = {
actions = [{
type = "weighted-forward"
target_groups = [{ target_group_key = "mytg2", weight = 1 }]
stickiness = { enabled = true, duration = 3600 }
}]
conditions = [{
host_header = {
values = [var.app2_dns_name]
}
}]
}
}
}

🔍 Explanation:

  • host_header: Matches requests based on the host field in the HTTP request header.
  • var.app1_dns_name / var.app2_dns_name: These should be set to app1.rezaops.com and app2.rezaops.com in your variables.
  • stickiness: ALB cookie-based stickiness still enabled.
  • weighted-forward: Each rule forwards traffic to its respective target group (mytg1 or mytg2).

🎯 Target Groups (No Change Here)

Target groups mytg1 and mytg2 remain the same. Each group listens on port 80 and includes its own health check:

mytg1 = {
name_prefix = "mytg1-"
port = 80
protocol = "HTTP"
target_type = "instance"
health_check = {
path = "/app1/index.html"
matcher = "200-399"
}
...
}
mytg2 = {
name_prefix = "mytg2-"
port = 80
protocol = "HTTP"
target_type = "instance"
health_check = {
path = "/app2/index.html"
matcher = "200-399"
}
...
}

Each app has its own health check path and group of EC2 instances.


🔗 EC2 Target Group Attachments

Also unchanged. We’re manually attaching the EC2s for App 1 and App 2:

resource "aws_lb_target_group_attachment" "mytg1" {
for_each = { for k, v in module.ec2_private_app1 : k => v }
target_group_arn = module.alb.target_groups["mytg1"].arn
target_id = each.value.id
port = 80
}
resource "aws_lb_target_group_attachment" "mytg2" {
for_each = { for k, v in module.ec2_private_app2 : k => v }
target_group_arn = module.alb.target_groups["mytg2"].arn
target_id = each.value.id
port = 80
}

✅ Summary

In this post, we updated our existing ALB setup to support Host-Based Routing instead of Path-Based. This setup is perfect when:

  • You want clean subdomain URLs (app1.example.com, app2.example.com)
  • You need to serve multiple apps under the same ALB using DNS rules
  • You want to build toward microservices or multi-tenant architectures

You’ve now built an ALB that handles:

  • HTTPS redirection
  • TLS with ACM
  • Listener rules using Host Headers
  • DNS aliasing with Route 53
  • Backend registration by EC2 group
Tags:
Write a comment