Back to blog
AWSBest PracticesCloud SecurityNetworkingOperations & Compliance

CloudFront Uses Outdated TLS Version: Detection and Fix

Learn why CloudFront accepting TLS 1.1 or older is a security risk, and how to enforce TLS 1.2 with CLI, Terraform, and policy-as-code guardrails.

TL;DR

This check flags CloudFront distributions that still accept TLS 1.1 or older from clients, exposing your traffic to known protocol weaknesses. Switch the viewer security policy to TLSv1.2_2021 to require modern TLS and close the gap.

CloudFront sits in front of a huge amount of internet-facing traffic, from static asset delivery to full application frontends. Because it terminates TLS for your viewers, the protocol versions it accepts directly determine how secure those connections are. If your distribution still accepts TLS 1.1 or older, you are letting clients negotiate down to protocols that the industry retired years ago.

The cloudfront_tlspolicy check looks at the minimum protocol version configured on each CloudFront distribution and fails any distribution where that floor is TLS 1.1 or lower.


What this check detects

Every CloudFront distribution has a viewer security policy, controlled by the MinimumProtocolVersion setting in the distribution's ViewerCertificate block. This setting defines the lowest TLS version a browser or API client is allowed to use when connecting to your distribution.

This check fails when MinimumProtocolVersion is set to one of the older policies that still permit TLS 1.1, TLS 1.0, or SSLv3:

  • SSLv3
  • TLSv1
  • TLSv1_2016
  • TLSv1.1_2016

It passes when the policy enforces TLS 1.2 or higher, such as TLSv1.2_2018, TLSv1.2_2019, or the current recommended value TLSv1.2_2021.

Note: The security policy only applies when you use a custom SSL certificate (ACM or imported IAM certificate). Distributions using the default *.cloudfront.net certificate always use TLS 1.2 as the minimum, so this setting is effectively locked. The check matters most for distributions serving custom domains.


Why it matters

TLS 1.0 and 1.1 are deprecated. RFC 8996 formally retired both in 2021, and all major browsers (Chrome, Firefox, Safari, Edge) dropped support around the same time. Continuing to accept them buys you nothing in terms of legitimate compatibility and introduces real risk.

Known protocol weaknesses

Older TLS versions carry a long list of documented attacks:

  • BEAST exploits the CBC cipher implementation in TLS 1.0.
  • POODLE targets SSLv3 and the fallback behavior that older clients rely on.
  • TLS 1.0 and 1.1 depend on MD5 and SHA-1 in their handshake, both of which are broken for collision resistance.
  • Neither version supports modern authenticated cipher suites like AES-GCM as a requirement, leaving you with weaker negotiated ciphers.

Compliance impact

PCI DSS has required disabling TLS 1.0 since 2018, and strongly discourages TLS 1.1. HIPAA, SOC 2, and most modern security frameworks expect TLS 1.2 as a baseline. If you handle payment data or run audits, an outdated viewer policy is a finding waiting to happen.

Warning: A downgrade attack only needs the weak option to be available. Even if 99 percent of your real traffic uses TLS 1.3, an attacker positioned on the network can force a vulnerable client down to TLS 1.0 as long as your distribution still accepts it. The fix is to remove the option entirely.


How to fix it

The remediation is straightforward: set the minimum protocol version to TLSv1.2_2021. There is rarely a reason to choose anything lower today.

Option 1: AWS Console

  1. Open the CloudFront console and select your distribution.
  2. Go to the General tab and click Edit under Settings.
  3. Confirm you are using a custom SSL certificate under Custom SSL certificate.
  4. Set Security policy to TLSv1.2_2021.
  5. Save changes. CloudFront will deploy the update across its edge locations.

Option 2: AWS CLI

Updating a distribution from the CLI requires fetching the current config, editing the ViewerCertificate.MinimumProtocolVersion field, and passing the ETag back with the update. First pull the config:

aws cloudfront get-distribution-config \
  --id E1234EXAMPLE \
  --output json > dist-config.json

# Note the ETag value printed at the top of the file

Edit dist-config.json so the ViewerCertificate block reads:

"ViewerCertificate": {
  "ACMCertificateArn": "arn:aws:acm:us-east-1:111122223333:certificate/abc123",
  "SSLSupportMethod": "sni-only",
  "MinimumProtocolVersion": "TLSv1.2_2021",
  "Certificate": "arn:aws:acm:us-east-1:111122223333:certificate/abc123",
  "CertificateSource": "acm"
}

Strip the outer DistributionConfig wrapper and ETag from the file so you are left with just the config object, then apply it:

aws cloudfront update-distribution \
  --id E1234EXAMPLE \
  --distribution-config file://dist-config.json \
  --if-match ETAG_VALUE_FROM_GET

Warning: Updating a distribution triggers a redeployment that takes several minutes to propagate to all edge locations. There is no downtime, but the change is not instant. Avoid scripting rapid back-to-back updates, as each one must reach Deployed status before the next.

Option 3: Terraform

If you manage CloudFront with Terraform, set minimum_protocol_version in the viewer_certificate block:

resource "aws_cloudfront_distribution" "site" {
  # ... origins, default_cache_behavior, etc ...

  viewer_certificate {
    acm_certificate_arn      = aws_acm_certificate.site.arn
    ssl_support_method       = "sni-only"
    minimum_protocol_version = "TLSv1.2_2021"
  }
}

For CloudFormation, the equivalent property under ViewerCertificate is MinimumProtocolVersion: TLSv1.2_2021.

Tip: Audit every distribution at once instead of clicking through the console. This one-liner lists each distribution ID alongside its current minimum protocol version so you can spot the stragglers fast.

aws cloudfront list-distributions \
  --query "DistributionList.Items[].{Id:Id, TLS:ViewerCertificate.MinimumProtocolVersion}" \
  --output table

How to prevent it from happening again

Fixing the distributions you have is only half the job. New distributions get created all the time, and defaults drift. Bake the requirement into your delivery pipeline.

Policy-as-code with Terraform

If you use Sentinel or Open Policy Agent (OPA) with Terraform, write a rule that rejects any distribution whose minimum protocol version is not at least TLS 1.2. Here is a Conftest-style Rego policy that fails old values:

package main

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_cloudfront_distribution"
  vc := resource.change.after.viewer_certificate[_]
  bad := {"SSLv3", "TLSv1", "TLSv1_2016", "TLSv1.1_2016"}
  bad[vc.minimum_protocol_version]
  msg := sprintf("CloudFront %s uses outdated TLS policy %s", [resource.address, vc.minimum_protocol_version])
}

Run it against a Terraform plan in CI:

terraform plan -out=tfplan.binary
terraform show -json tfplan.binary > tfplan.json
conftest test tfplan.json

Catch it before merge

Add the above as a required status check on pull requests. A distribution with a weak TLS policy never reaches production because the build fails first. Pair this with Lensix continuous scanning so anything created out-of-band, through the console or a one-off script, gets flagged within minutes.

Tip: Define a shared Terraform module for CloudFront distributions with minimum_protocol_version hardcoded to TLSv1.2_2021 and no override variable. Teams that consume the module get a compliant default with zero effort and no way to accidentally weaken it.


Best practices

  • Standardize on TLSv1.2_2021. It enforces TLS 1.2 as the floor and uses a modern cipher suite list. Clients that support TLS 1.3 will still negotiate it, since the setting defines a minimum, not a cap.
  • Use SNI, not dedicated IP. Set ssl_support_method to sni-only unless you have legacy clients that genuinely cannot do SNI. Dedicated IP custom SSL carries a significant monthly charge and almost no one needs it anymore.
  • Inventory regularly. Distributions accumulate over years. Run the list command above on a schedule or rely on automated scanning so old policies do not quietly persist.
  • Test before assuming compatibility. If you are worried about cutting off legacy clients, check your access logs for the negotiated TLS version field before and after. In practice, anything that cannot do TLS 1.2 is a bot or an end-of-life device.
  • Apply the same scrutiny to origins. The viewer policy covers client-to-CloudFront. Make sure your origin protocol policy and any load balancers behind CloudFront also enforce TLS 1.2 so the connection is encrypted modernly end to end.

Tightening the viewer security policy is one of the cheapest security wins in AWS. There is no cost, no downtime, and no meaningful compatibility loss, just a closed door that should have been closed years ago.