CloudFront distributions should use the recommended TLS security policy
CloudFront distributions that allow TLS 1.0 or 1.1 expose traffic to known protocol weaknesses including BEAST, POODLE, and downgrade attacks. Both versions were formally deprecated by RFC 8996 in March 2021, and major browsers have dropped support for them. Setting TLSv1.2_2021 as the minimum restricts connections to TLS 1.2 with strong ciphers, eliminating CBC-mode cipher suites that have been repeatedly targeted.
Running an older policy also creates friction during audits and penetration tests, where anything below TLS 1.2 gets flagged as a finding regardless of compensating controls. Enforcing the policy at the distribution level avoids that overhead entirely.
Retrofit consideration
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 "cloudfront" {
source = "registry.compliance.tf/terraform-aws-modules/cloudfront/aws"
version = ">=6.0.0,<7.0.0"
}This control is enforced automatically with Compliance.tf modules. Start free trial
If you use terraform-aws-modules/cloudfront/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 "cloudfront" {
source = "terraform-aws-modules/cloudfront/aws"
version = ">=6.0.0,<7.0.0"
viewer_certificate = {
minimum_protocol_version = "TLSv1.2_2021"
}
}Use AWS provider resources directly. See docs for the resources involved: aws_cloudfront_distribution.
resource "aws_cloudfront_distribution" "this" {
default_cache_behavior {
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
field_level_encryption_id = "abc123"
forwarded_values {
cookies {
forward = "none"
}
query_string = false
}
target_origin_id = "S3Origin"
viewer_protocol_policy = "redirect-to-https"
}
default_root_object = "index.html"
enabled = true
logging_config {
bucket = "example-bucket-abc123"_domain_name
}
origin {
domain_name = "example.s3.amazonaws.com"
origin_id = "S3Origin"
}
restrictions {
geo_restriction {
locations = ["US", "CA", "GB"]
restriction_type = "whitelist"
}
}
wait_for_deployment = false
web_acl_id = "arn:aws:wafv2:us-east-1:123456789012:regional/webacl/example/12345678-1234-1234-1234-123456789012"
viewer_certificate {
acm_certificate_arn = "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
minimum_protocol_version = "TLSv1.2_2021"
ssl_support_method = "sni-only"
}
}What this control checks
In the aws_cloudfront_distribution resource, viewer_certificate must set minimum_protocol_version to "TLSv1.2_2021" or a newer policy. "TLSv1.2_2019" is older and fails this control because it still permits weaker cipher suites. When cloudfront_default_certificate is true (the *.cloudfront.net domain), AWS manages the TLS policy and minimum_protocol_version is silently ignored, which may still cause a failure depending on the evaluator. For a reliable pass, use a custom ACM certificate: set acm_certificate_arn, ssl_support_method = "sni-only", and minimum_protocol_version = "TLSv1.2_2021". Any of the following values fail: "TLSv1", "TLSv1_2016", "TLSv1.1_2016", "TLSv1.2_2018", or an omitted minimum_protocol_version.
Common pitfalls
Default certificate ignores minimum_protocol_version
Using cloudfront_default_certificate = true hands TLS policy control to AWS, so minimum_protocol_version is silently ignored. Distributions using the shared *.cloudfront.net certificate may still fail this control. Switch to a custom ACM certificate with acm_certificate_arn to explicitly own the policy setting.
Confusing policy name ordering
All policies named TLSv1.2_* require TLS 1.2, but they differ on cipher suites. TLSv1.2_2018 and TLSv1.2_2019 both still permit CBC ciphers. TLSv1.2_2021 is the first in the series to remove them entirely. The year suffix matters.
Legacy vip ssl_support_method requires dedicated IP
The "vip" value for ssl_support_method provisions a dedicated IP address for SSL and incurs extra CloudFront charges, but has no effect on which TLS version is negotiated. It's a common mistake when first adding a custom certificate: old Terraform snippets often specify "vip" without flagging the cost implication. Use "sni-only" unless you have a specific reason not to.
Terraform default value may not match recommended policy
Omit minimum_protocol_version and behavior falls back to AWS defaults, which vary by certificate mode and may not satisfy this control. Always set it explicitly in the viewer_certificate block.
Audit evidence
Auditors expect Config rule evaluation results showing each distribution as COMPLIANT, or Security Hub findings with status PASSED for the relevant CloudFront controls. The CloudFront console Security Policy column should list TLSv1.2_2021 or later for every distribution. The CLI command aws cloudfront get-distribution-config --id <ID> returns ViewerCertificate.MinimumProtocolVersion for point-in-time verification. Historical compliance comes from Config timeline snapshots or periodic Security Hub exports stored in S3.
Related controls
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
- Compliance.tf Control:
cloudfront_distribution_uses_recommended_tls_security_policy - AWS Config Managed Rules:
CLOUDFRONT_SECURITY_POLICY_CHECK,CLOUDFRONT_SSL_POLICY_CHECK - Checkov Check:
CKV_AWS_174 - Powerpipe Control:
aws_compliance.control.cloudfront_distribution_uses_recommended_tls_security_policy - AWS Security Hub Control:
CloudFront.15 - KICS Queries:
00e5e55e-c2ff-46b3-a757-a7a1cd802456,3a1e94df-6847-4c0e-a3b6-6c6af4e128ef - Trivy Check:
AWS-0013
Last reviewed: 2026-03-09