Skip to content

ELB application and network load balancers should only use SSL or HTTPS listeners

Load balancers sit at the boundary between clients and your internal services. An HTTP or plain TCP listener exposes every request, including authentication tokens, session cookies, and API payloads, to interception anywhere along the network path. TLS termination at the load balancer with an ACM certificate gives you automated renewal, removes the manual certificate management overhead, and ensures client traffic is encrypted by default.

Misconfigured listeners are common in development environments that get promoted to production without a security pass. A single unencrypted listener on a public-facing ALB can result in credential theft or regulatory penalties.

Retrofit consideration

Existing HTTP listeners require redirect actions to HTTPS, new ACM certificates, and DNS validation records before the old listeners can be removed. Clients hardcoded to HTTP endpoints may break if redirects are not configured alongside the protocol change.

Implementation

Choose the approach that matches how you manage Terraform.

Use the compliance.tf module to enforce this control by default. See get started with compliance.tf.

module "alb" {
  source  = "pcidss.compliance.tf/terraform-aws-modules/alb/aws"
  version = ">=10.0.0,<11.0.0"

  access_logs = {
    bucket  = "example-bucket-abc123"
    enabled = true
  }
  internal = true
  listeners = {
    https = {
      certificate_arn = "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
      forward = {
        target_group_key = "default"
      }
      protocol   = "HTTPS"
      ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
    }
  }
  load_balancer_type = "application"
  name               = "abc123"
  security_groups    = ["sg-abc12345"]
  subnets            = ["subnet-abc123", "subnet-def456"]
  target_groups = {
    default = {
      create_attachment = false
      name_prefix       = "def-"
      port              = 443
      protocol          = "HTTPS"
      target_type       = "ip"
    }
  }
  vpc_id = "vpc-12345678"
}

module "alb" {
  source  = "hipaa.compliance.tf/terraform-aws-modules/alb/aws"
  version = ">=10.0.0,<11.0.0"

  access_logs = {
    bucket  = "example-bucket-abc123"
    enabled = true
  }
  internal = true
  listeners = {
    https = {
      certificate_arn = "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
      forward = {
        target_group_key = "default"
      }
      protocol   = "HTTPS"
      ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
    }
  }
  load_balancer_type = "application"
  name               = "abc123"
  security_groups    = ["sg-abc12345"]
  subnets            = ["subnet-abc123", "subnet-def456"]
  target_groups = {
    default = {
      create_attachment = false
      name_prefix       = "def-"
      port              = 443
      protocol          = "HTTPS"
      target_type       = "ip"
    }
  }
  vpc_id = "vpc-12345678"
}

module "alb" {
  source  = "nist80053.compliance.tf/terraform-aws-modules/alb/aws"
  version = ">=10.0.0,<11.0.0"

  access_logs = {
    bucket  = "example-bucket-abc123"
    enabled = true
  }
  internal = true
  listeners = {
    https = {
      certificate_arn = "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
      forward = {
        target_group_key = "default"
      }
      protocol   = "HTTPS"
      ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
    }
  }
  load_balancer_type = "application"
  name               = "abc123"
  security_groups    = ["sg-abc12345"]
  subnets            = ["subnet-abc123", "subnet-def456"]
  target_groups = {
    default = {
      create_attachment = false
      name_prefix       = "def-"
      port              = 443
      protocol          = "HTTPS"
      target_type       = "ip"
    }
  }
  vpc_id = "vpc-12345678"
}

module "alb" {
  source  = "nistcsf.compliance.tf/terraform-aws-modules/alb/aws"
  version = ">=10.0.0,<11.0.0"

  access_logs = {
    bucket  = "example-bucket-abc123"
    enabled = true
  }
  internal = true
  listeners = {
    https = {
      certificate_arn = "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
      forward = {
        target_group_key = "default"
      }
      protocol   = "HTTPS"
      ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
    }
  }
  load_balancer_type = "application"
  name               = "abc123"
  security_groups    = ["sg-abc12345"]
  subnets            = ["subnet-abc123", "subnet-def456"]
  target_groups = {
    default = {
      create_attachment = false
      name_prefix       = "def-"
      port              = 443
      protocol          = "HTTPS"
      target_type       = "ip"
    }
  }
  vpc_id = "vpc-12345678"
}

module "alb" {
  source  = "fedrampmoderate.compliance.tf/terraform-aws-modules/alb/aws"
  version = ">=10.0.0,<11.0.0"

  access_logs = {
    bucket  = "example-bucket-abc123"
    enabled = true
  }
  internal = true
  listeners = {
    https = {
      certificate_arn = "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
      forward = {
        target_group_key = "default"
      }
      protocol   = "HTTPS"
      ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
    }
  }
  load_balancer_type = "application"
  name               = "abc123"
  security_groups    = ["sg-abc12345"]
  subnets            = ["subnet-abc123", "subnet-def456"]
  target_groups = {
    default = {
      create_attachment = false
      name_prefix       = "def-"
      port              = 443
      protocol          = "HTTPS"
      target_type       = "ip"
    }
  }
  vpc_id = "vpc-12345678"
}

module "alb" {
  source  = "nist800171.compliance.tf/terraform-aws-modules/alb/aws"
  version = ">=10.0.0,<11.0.0"

  access_logs = {
    bucket  = "example-bucket-abc123"
    enabled = true
  }
  internal = true
  listeners = {
    https = {
      certificate_arn = "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
      forward = {
        target_group_key = "default"
      }
      protocol   = "HTTPS"
      ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
    }
  }
  load_balancer_type = "application"
  name               = "abc123"
  security_groups    = ["sg-abc12345"]
  subnets            = ["subnet-abc123", "subnet-def456"]
  target_groups = {
    default = {
      create_attachment = false
      name_prefix       = "def-"
      port              = 443
      protocol          = "HTTPS"
      target_type       = "ip"
    }
  }
  vpc_id = "vpc-12345678"
}

module "alb" {
  source  = "cisacyberessentials.compliance.tf/terraform-aws-modules/alb/aws"
  version = ">=10.0.0,<11.0.0"

  access_logs = {
    bucket  = "example-bucket-abc123"
    enabled = true
  }
  internal = true
  listeners = {
    https = {
      certificate_arn = "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
      forward = {
        target_group_key = "default"
      }
      protocol   = "HTTPS"
      ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
    }
  }
  load_balancer_type = "application"
  name               = "abc123"
  security_groups    = ["sg-abc12345"]
  subnets            = ["subnet-abc123", "subnet-def456"]
  target_groups = {
    default = {
      create_attachment = false
      name_prefix       = "def-"
      port              = 443
      protocol          = "HTTPS"
      target_type       = "ip"
    }
  }
  vpc_id = "vpc-12345678"
}

module "alb" {
  source  = "nydfs23.compliance.tf/terraform-aws-modules/alb/aws"
  version = ">=10.0.0,<11.0.0"

  access_logs = {
    bucket  = "example-bucket-abc123"
    enabled = true
  }
  internal = true
  listeners = {
    https = {
      certificate_arn = "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
      forward = {
        target_group_key = "default"
      }
      protocol   = "HTTPS"
      ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
    }
  }
  load_balancer_type = "application"
  name               = "abc123"
  security_groups    = ["sg-abc12345"]
  subnets            = ["subnet-abc123", "subnet-def456"]
  target_groups = {
    default = {
      create_attachment = false
      name_prefix       = "def-"
      port              = 443
      protocol          = "HTTPS"
      target_type       = "ip"
    }
  }
  vpc_id = "vpc-12345678"
}

module "alb" {
  source  = "ffiec.compliance.tf/terraform-aws-modules/alb/aws"
  version = ">=10.0.0,<11.0.0"

  access_logs = {
    bucket  = "example-bucket-abc123"
    enabled = true
  }
  internal = true
  listeners = {
    https = {
      certificate_arn = "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
      forward = {
        target_group_key = "default"
      }
      protocol   = "HTTPS"
      ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
    }
  }
  load_balancer_type = "application"
  name               = "abc123"
  security_groups    = ["sg-abc12345"]
  subnets            = ["subnet-abc123", "subnet-def456"]
  target_groups = {
    default = {
      create_attachment = false
      name_prefix       = "def-"
      port              = 443
      protocol          = "HTTPS"
      target_type       = "ip"
    }
  }
  vpc_id = "vpc-12345678"
}

module "alb" {
  source  = "eugmpannex11.compliance.tf/terraform-aws-modules/alb/aws"
  version = ">=10.0.0,<11.0.0"

  access_logs = {
    bucket  = "example-bucket-abc123"
    enabled = true
  }
  internal = true
  listeners = {
    https = {
      certificate_arn = "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
      forward = {
        target_group_key = "default"
      }
      protocol   = "HTTPS"
      ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
    }
  }
  load_balancer_type = "application"
  name               = "abc123"
  security_groups    = ["sg-abc12345"]
  subnets            = ["subnet-abc123", "subnet-def456"]
  target_groups = {
    default = {
      create_attachment = false
      name_prefix       = "def-"
      port              = 443
      protocol          = "HTTPS"
      target_type       = "ip"
    }
  }
  vpc_id = "vpc-12345678"
}

module "alb" {
  source  = "cfrpart11.compliance.tf/terraform-aws-modules/alb/aws"
  version = ">=10.0.0,<11.0.0"

  access_logs = {
    bucket  = "example-bucket-abc123"
    enabled = true
  }
  internal = true
  listeners = {
    https = {
      certificate_arn = "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
      forward = {
        target_group_key = "default"
      }
      protocol   = "HTTPS"
      ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
    }
  }
  load_balancer_type = "application"
  name               = "abc123"
  security_groups    = ["sg-abc12345"]
  subnets            = ["subnet-abc123", "subnet-def456"]
  target_groups = {
    default = {
      create_attachment = false
      name_prefix       = "def-"
      port              = 443
      protocol          = "HTTPS"
      target_type       = "ip"
    }
  }
  vpc_id = "vpc-12345678"
}

module "alb" {
  source  = "rbicybersecurity.compliance.tf/terraform-aws-modules/alb/aws"
  version = ">=10.0.0,<11.0.0"

  access_logs = {
    bucket  = "example-bucket-abc123"
    enabled = true
  }
  internal = true
  listeners = {
    https = {
      certificate_arn = "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
      forward = {
        target_group_key = "default"
      }
      protocol   = "HTTPS"
      ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
    }
  }
  load_balancer_type = "application"
  name               = "abc123"
  security_groups    = ["sg-abc12345"]
  subnets            = ["subnet-abc123", "subnet-def456"]
  target_groups = {
    default = {
      create_attachment = false
      name_prefix       = "def-"
      port              = 443
      protocol          = "HTTPS"
      target_type       = "ip"
    }
  }
  vpc_id = "vpc-12345678"
}

module "alb" {
  source  = "rbiitfnbfc.compliance.tf/terraform-aws-modules/alb/aws"
  version = ">=10.0.0,<11.0.0"

  access_logs = {
    bucket  = "example-bucket-abc123"
    enabled = true
  }
  internal = true
  listeners = {
    https = {
      certificate_arn = "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
      forward = {
        target_group_key = "default"
      }
      protocol   = "HTTPS"
      ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
    }
  }
  load_balancer_type = "application"
  name               = "abc123"
  security_groups    = ["sg-abc12345"]
  subnets            = ["subnet-abc123", "subnet-def456"]
  target_groups = {
    default = {
      create_attachment = false
      name_prefix       = "def-"
      port              = 443
      protocol          = "HTTPS"
      target_type       = "ip"
    }
  }
  vpc_id = "vpc-12345678"
}

module "alb" {
  source  = "nistcsfv11.compliance.tf/terraform-aws-modules/alb/aws"
  version = ">=10.0.0,<11.0.0"

  access_logs = {
    bucket  = "example-bucket-abc123"
    enabled = true
  }
  internal = true
  listeners = {
    https = {
      certificate_arn = "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
      forward = {
        target_group_key = "default"
      }
      protocol   = "HTTPS"
      ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
    }
  }
  load_balancer_type = "application"
  name               = "abc123"
  security_groups    = ["sg-abc12345"]
  subnets            = ["subnet-abc123", "subnet-def456"]
  target_groups = {
    default = {
      create_attachment = false
      name_prefix       = "def-"
      port              = 443
      protocol          = "HTTPS"
      target_type       = "ip"
    }
  }
  vpc_id = "vpc-12345678"
}

module "alb" {
  source  = "pcidssv321.compliance.tf/terraform-aws-modules/alb/aws"
  version = ">=10.0.0,<11.0.0"

  access_logs = {
    bucket  = "example-bucket-abc123"
    enabled = true
  }
  internal = true
  listeners = {
    https = {
      certificate_arn = "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
      forward = {
        target_group_key = "default"
      }
      protocol   = "HTTPS"
      ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
    }
  }
  load_balancer_type = "application"
  name               = "abc123"
  security_groups    = ["sg-abc12345"]
  subnets            = ["subnet-abc123", "subnet-def456"]
  target_groups = {
    default = {
      create_attachment = false
      name_prefix       = "def-"
      port              = 443
      protocol          = "HTTPS"
      target_type       = "ip"
    }
  }
  vpc_id = "vpc-12345678"
}

If you use terraform-aws-modules/alb/aws, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.

module "alb" {
  source  = "terraform-aws-modules/alb/aws"
  version = ">=10.0.0,<11.0.0"

  access_logs = {
    bucket  = "example-bucket-abc123"
    enabled = true
  }
  internal = true
  listeners = {
    https = {
      certificate_arn = "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
      forward = {
        target_group_key = "default"
      }
      protocol   = "HTTPS"
      ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
    }
  }
  load_balancer_type = "application"
  name               = "abc123"
  security_groups    = ["sg-abc12345"]
  subnets            = ["subnet-abc123", "subnet-def456"]
  target_groups = {
    default = {
      create_attachment = false
      name_prefix       = "def-"
      port              = 443
      protocol          = "HTTPS"
      target_type       = "ip"
    }
  }
  vpc_id = "vpc-12345678"
}

Use AWS provider resources directly. See docs for the resources involved: aws_lb_listener.

resource "aws_lb_listener" "this" {
  certificate_arn = "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"

  default_action {
    fixed_response {
      content_type = "text/plain"
      message_body = "OK"
      status_code  = "200"
    }
    type = "fixed-response"
  }

  load_balancer_arn = "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/net/example/1234567890123456"
  port              = 443
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-TLS13-1-2-2021-06"
}

What this control checks

This control checks every aws_lb_listener resource on an ALB (load_balancer_type = "application") or NLB (load_balancer_type = "network"). For ALB listeners, protocol must be "HTTPS" (port 443 by convention). For NLB listeners, protocol must be "TLS". In both cases, certificate_arn must reference a valid ACM certificate ARN, not an IAM server certificate. Listeners with protocol = "HTTP" or protocol = "TCP" serving application traffic directly will fail. Additional certificates attached via aws_lb_listener_certificate are acceptable, but the primary certificate_arn on the listener itself must be present and point to ACM.

Common pitfalls

  • HTTP listeners without redirect actions flagged as failures

    An HTTP listener on port 80 only passes this control if its default_action specifies type = "redirect" with redirect { protocol = "HTTPS" }. Without that redirect block, the policy engine treats it as serving unencrypted traffic and fails it. The redirect must be explicit in the Terraform resource, not assumed from downstream behavior.

  • IAM server certificates do not satisfy ACM requirement

    certificate_arn pointing to an IAM-uploaded cert (arn:aws:iam::...) fails this control regardless of whether the certificate itself is valid. The ARN must come from ACM (arn:aws:acm::...). Migrate to aws_acm_certificate with DNS validation to get automated renewal at the same time.

  • Deprecated aws_alb alias can obscure listener review

    If your configuration still uses aws_alb (a deprecated alias of aws_lb), audits targeting aws_lb_listener resources should confirm they are reviewing the right type. The listener protocol and certificate_arn live on aws_lb_listener resources regardless of which load balancer alias appears in your config. Don't let the alias be a reason listener resources get skipped during review.

  • NLB TCP passthrough mistaken for TLS

    NLB TCP passthrough is a common source of confusion. protocol = "TCP" means the load balancer forwards bytes without inspecting or terminating TLS. Even if the backend terminates TLS itself, this control fails because the NLB listener must carry protocol = "TLS" with a certificate_arn for the load balancer to validate the certificate.

Audit evidence

Config evaluation results showing all ALB and NLB listener resources as COMPLIANT are the primary evidence artifact. Console screenshots of each listener's configuration tab, showing the HTTPS or TLS protocol and the associated ACM certificate ARN, provide direct confirmation. CloudTrail CreateListener and ModifyListener events establish that listeners were created with encrypted protocols and that no subsequent modification downgraded them to HTTP or TCP.

Security Hub findings mapped to this control should show PASSED across all accounts and regions where load balancers are deployed.

Framework-specific interpretation

PCI DSS v4.0: For merchants and service providers handling cardholder data, Requirement 4.2.1 is explicit: strong cryptography on all transmissions over open, public networks. Load balancers fronting payment services must terminate TLS with trusted certificates, and ACM certificates from a publicly trusted CA satisfy the non-expired, valid-certificate condition that assessors check.

HIPAA Omnibus Rule 2013: 45 CFR 164.312(e)(1) requires a mechanism to guard against unauthorized access to ePHI during transmission. Any load balancer routing health-related traffic is in scope. TLS termination with an ACM certificate at the ALB or NLB boundary covers the encryption piece of that requirement, keeping ePHI out of cleartext on the wire.

NIST SP 800-53 Rev 5: SC-8(1) calls for cryptographic mechanisms that prevent both unauthorized disclosure and undetected modification of data in transit, not just channel encryption. ACM-managed certificates with automated renewal satisfy the trusted-certificate requirement, and this control confirms SC-8 and its (1) enhancement are addressed at the listener level.

NIST Cybersecurity Framework v2.0: PR.DS covers data security under the Protect function. Enforcing TLS or HTTPS on every listener is one straightforward way to satisfy the data-in-transit protection objective for load balancer traffic.

FedRAMP Moderate Baseline Rev 4: SC-8 and SC-23 together cover transmission confidentiality, integrity, and session authenticity for federal systems. At the Moderate baseline, every load balancer listener serving federal information needs TLS termination with a trusted certificate. ACM handles the certificate lifecycle; the listener configuration is where you prove it is applied.

Tool mappings

Use these identifiers to cross-reference this control across tools, reports, and evidence.

  • Compliance.tf Control: elb_application_network_lb_use_ssl_certificate

  • AWS Config Managed Rules: ELBV2_ACM_CERTIFICATE_REQUIRED, ELBV2_LISTENER_ENCRYPTION_IN_TRANSIT

  • Checkov Check: CKV_AWS_127

  • Powerpipe Control: aws_compliance.control.elb_application_network_lb_use_ssl_certificate

  • Prowler Check: elb_ssl_listeners_use_acm_certificate

  • AWS Security Hub Control: ELB.18

Last reviewed: 2026-03-09