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

Secure Your Apps with ALB Path-Based Routing and HTTPS using Terraform

June 2, 2025 AWS, DevOps, Infrastructure, Terraform
Secure Your Apps with ALB Path-Based Routing and HTTPS using Terraform

If you’ve followed along with Part 1 and Part 2 and Part 3 of my AWS-Terraform series, you’ve already built a solid VPC, deployed EC2 instances, and configured a basic ALB.

Now it’s time to bring your infrastructure closer to production-grade. In this tutorial, we’ll:

✅ Add TLS/SSL support with AWS ACM
✅ Create HTTPS listeners
✅ Route traffic based on URL paths like /app1 and /app2
✅ Attach multiple EC2 groups to the ALB
✅ Configure DNS with Route 53

Let’s break it down step by step.

✅ ACM Module – TLS Certificate for HTTPS

module "acm" {
source = "terraform-aws-modules/acm/aws"
version = "5.1.0"
  • source: Uses the official ACM module from Terraform Registry to simplify certificate provisioning.
  • version: Locks the module to a specific version for stability.
  domain_name  = trimsuffix(data.aws_route53_zone.mydomain.name, ".")
zone_id = data.aws_route53_zone.mydomain.zone_id
  • domain_name: Main domain to issue the cert for. trimsuffix() removes the trailing dot.
  • zone_id: The ID of your Route 53 hosted zone where validation records will be created.
  subject_alternative_names = ["*.rezaops.com"]
  • Adds a wildcard certificate, allowing TLS on subdomains like apps.rezaops.com.
  validation_method    = "DNS"
wait_for_validation = true
  • validation_method: Uses DNS validation (recommended for automation).
  • wait_for_validation: Waits for Route 53 to validate the cert before continuing.
tags = local.common_tags
}
  • Adds tags like Environment, Owner, etc.

📤 ACM Output Block

output "acm_certificate_arn" {
description = "The ARN of the certificate"
value = module.acm.acm_certificate_arn
}
  • Outputs the Amazon Resource Name of the issued cert.
  • This value is passed into the ALB module for HTTPS listener setup.

🌐 ALB Module – Application Load Balancer Setup

module "alb" {
source = "terraform-aws-modules/alb/aws"
version = "9.11.0"
  • Uses the ALB module to create listeners, target groups, and routing rules.
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
  • name: Sets ALB name based on a local value.
  • load_balancer_type: Application Load Balancer (Layer 7).
  • vpc_id: Attaches ALB to the right VPC.
  • subnets: Deploys ALB across public subnets.
  • security_groups: Attaches an SG allowing ports 80 & 443.
  • deletion_protection: Disabled here for dev/testing.

🔁 ALB Listener Block: HTTP → HTTPS Redirect

my-http-https-redirect = {
port = 80
protocol = "HTTP"
redirect = {
port = "443"
protocol = "HTTPS"
status_code = "HTTP_301"
}
}
  • Redirects all insecure HTTP traffic to secure HTTPS
  • status_code = HTTP_301: Permanent redirect (good for SEO & security)

🔒 ALB Listener Block: HTTPS + Path-Based Routing

my-https-listener = {
port = 443
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-TLS13-1-2-Res-2021-06"
certificate_arn = module.acm.acm_certificate_arn
  • Listens on port 443
  • SSL policy: Uses TLS 1.3 with modern cipher suite
  • certificate_arn: Points to the ACM cert output earlier

🧪 HTTPS Fixed Response for Root Path

fixed_response = {
content_type = "text/plain"
message_body = "Fixed Static message - for Root Context"
status_code = "200"
}
  • This sends a fixed response if someone visits the base domain /
  • Optional and often used for placeholder or landing messages

🎯 Path-Based Listener Rules

Rule 1: For /app1

myapp1-rule = {
actions = [{
type = "weighted-forward"
target_groups = [{
target_group_key = "mytg1"
weight = 1
}]
stickiness = {
enabled = true
duration = 3600
}
}]
conditions = [{
path_pattern = {
values = ["/app1*"]
}
}]
}
  • path_pattern: Matches URLs like /app1, /app1/home, etc.
  • weighted-forward: Sends all traffic to mytg1
  • stickiness: Ensures the same user gets the same backend for 1 hour

Rule 2: For /app2 (same logic)

myapp2-rule = {
actions = [{
type = "weighted-forward"
target_groups = [{
target_group_key = "mytg2"
weight = 1
}]
stickiness = {
enabled = true
duration = 3600
}
}]
conditions = [{
path_pattern = {
values = ["/app2*"]
}
}]
}

🧱 Target Groups for Each App

mytg1 – for /app1

mytg1 = {
create_attachment = false
name_prefix = "mytg1-"
port = 80
protocol = "HTTP"
target_type = "instance"
protocol_version = "HTTP1"
health_check = {
enabled = true
path = "/app1/index.html"
matcher = "200-399"
}
tags = local.common_tags
}
  • create_attachment = false: We’ll attach EC2s manually
  • health_check path must match a real file or endpoint in your app

mytg2 – for /app2 (same logic)

mytg2 = {
...
path = "/app2/index.html"
}

🔗 Target Group Attachments (Manual)

Attach EC2s for /app1

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
}
  • Registers each EC2 in module.ec2_private_app1 to mytg1

Attach EC2s for /app2

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
}
  • Same logic, different app group

🌍 Route 53 DNS Record

resource "aws_route53_record" "apps_dns" {
zone_id = data.aws_route53_zone.mydomain.zone_id
name = "apps.rezaops.com"
type = "A"

alias {
name = module.alb.dns_name
zone_id = module.alb.zone_id
evaluate_target_health = true
}
}

📘 Explanation:

  • Points apps.rezaops.com to the ALB using an alias record
  • evaluate_target_health: If targets are unhealthy, Route 53 won’t route traffic

✅ Wrapping Up

In this article, you learned how to:

  • Issue a TLS certificate using AWS ACM and DNS validation
  • Configure an ALB with HTTP to HTTPS redirection
  • Set up path-based routing rules for different applications
  • Create and attach multiple target groups
  • Route traffic securely with a custom domain using Route 53

This setup is ideal for hosting multiple microservices or apps under a single domain with clean separation, secure routing, and strong observability options through ALB features.

🔜 Coming Next

In the next part of this series, I’ll walk you through setting up Host Header-Based Routing with your ALB.

You’ll learn how to:

  • Route traffic to different target groups based on subdomains like app1.rezaops.com, app2.rezaops.com
  • Add multiple SSL certificates (optional: SNI support)
  • Update DNS records for clean and secure multi-host deployments

This approach is commonly used in multi-tenant architectures and is perfect when hosting multiple applications behind the same ALB.

Tags:
Write a comment