Back to blog
AWSBest PracticesCloud SecurityNetworking

CloudFront Allows Unencrypted HTTP: Why It Matters and How to Fix It

Learn why CloudFront allowing unencrypted HTTP is a security risk, how to enforce HTTPS with the viewer protocol policy, and how to prevent regressions with IaC.

TL;DR

This check flags CloudFront distributions that accept plain HTTP, leaving traffic open to interception and tampering. Set the viewer protocol policy to redirect-to-https or https-only on every cache behavior to fix it.

CloudFront sits at the edge of your infrastructure, terminating connections from users all over the world before forwarding requests to your origin. That makes it one of the most exposed components you run. If a distribution accepts unencrypted HTTP, every byte between your users and the edge can be read or modified by anyone in the network path. This check exists to catch that gap before someone else does.

The cloudfront_http check inspects each cache behavior on a distribution and reports any that allow HTTP requests instead of forcing HTTPS.


What this check detects

A CloudFront distribution has one default cache behavior plus zero or more path-based behaviors. Each behavior carries a viewer protocol policy that controls how CloudFront responds to viewer (client) requests. There are three possible values:

  • allow-all — HTTP and HTTPS are both accepted, with no redirect. This is what the check flags.
  • redirect-to-https — HTTP requests get a 301 redirect to the HTTPS equivalent.
  • https-only — HTTP requests are rejected outright with a 403.

If any behavior on the distribution is set to allow-all, the check fails. It does not matter whether your origin is an S3 bucket, an ALB, or a custom server. The risk is in the viewer-facing leg of the connection.

Note: The viewer protocol policy is separate from the origin protocol policy. The first controls client-to-CloudFront, the second controls CloudFront-to-origin. Both should use HTTPS, but this check is specifically about the viewer side.


Why it matters

Allowing HTTP is not a theoretical problem. Here is what it actually opens you up to.

Credential and session theft

If a login form, an API token, or a session cookie travels over HTTP even once, an attacker on the same network (public Wi-Fi, a compromised router, a malicious ISP) can read it in plaintext. With allow-all, a user who types example.com into a browser without the scheme will hit HTTP first, and that first request can leak everything before any redirect would have happened.

Content injection

HTTP responses can be modified in transit. An attacker can inject malicious JavaScript into a page, swap out download links, or insert ads and tracking. Your users see your domain in the address bar and trust the content, but it was tampered with downstream.

SSL stripping

Tools like sslstrip exploit exactly this configuration. When a distribution accepts both protocols, an active attacker can keep a victim on HTTP and quietly proxy their traffic, stripping the upgrade to HTTPS. The victim never sees the lock icon and often does not notice.

Warning: A redirect from HTTP to HTTPS still exposes the very first request in plaintext. That request includes the full URL and any data already in the path or query string. Combine redirect-to-https with HSTS so browsers skip the insecure hop on repeat visits.

Compliance failures

PCI DSS, HIPAA, SOC 2, and most modern security baselines require encryption in transit. A distribution serving cardholder data or PHI over HTTP is a finding that auditors will flag immediately. It is also one of the easiest findings to remediate, so leaving it open looks careless.


How to fix it

You need to change the viewer protocol policy on every cache behavior. Choose redirect-to-https if you have legacy clients that might still request HTTP and you want them to keep working, or https-only if you want to reject HTTP outright.

Option 1: AWS Console

  1. Open the CloudFront console and select your distribution.
  2. Go to the Behaviors tab.
  3. Select the default behavior, click Edit.
  4. Under Viewer protocol policy, choose Redirect HTTP to HTTPS or HTTPS only.
  5. Save changes, then repeat for every other behavior in the list.

Option 2: AWS CLI

CloudFront updates require you to fetch the current config, modify it, and push it back with the matching ETag. Start by pulling the config:

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

Note the ETag value at the top of the output. Edit dist-config.json and set ViewerProtocolPolicy to redirect-to-https in the DefaultCacheBehavior and inside every item under CacheBehaviors. Then strip the outer ETag and DistributionConfig wrapper so the file contains just the config object.

Danger: update-distribution replaces the entire distribution config. If your edited file is missing a field, you will wipe that setting. Always start from the exact output of get-distribution-config and change only the protocol policy values.

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

The change deploys to all edge locations over a few minutes. The distribution status moves from InProgress to Deployed when it is live.

Option 3: Terraform

If you manage CloudFront with Terraform, set the policy on the default behavior and any ordered behaviors:

resource "aws_cloudfront_distribution" "site" {
  # ... origin and other config ...

  default_cache_behavior {
    target_origin_id       = "s3-origin"
    viewer_protocol_policy = "redirect-to-https"
    allowed_methods        = ["GET", "HEAD"]
    cached_methods         = ["GET", "HEAD"]
    # ... cache policy etc ...
  }

  ordered_cache_behavior {
    path_pattern           = "/api/*"
    target_origin_id       = "alb-origin"
    viewer_protocol_policy = "https-only"
    allowed_methods        = ["GET", "HEAD", "OPTIONS", "POST", "PUT", "DELETE"]
    cached_methods         = ["GET", "HEAD"]
    # ...
  }
}

Run terraform plan to confirm the change, then terraform apply.

Tip: Pair the redirect with a Strict-Transport-Security header. Attach a CloudFront response headers policy with strict_transport_security set to a long max-age so browsers refuse HTTP on future visits without making the insecure request at all.

resource "aws_cloudfront_response_headers_policy" "hsts" {
  name = "hsts-policy"

  security_headers_config {
    strict_transport_security {
      access_control_max_age_sec = 63072000
      include_subdomains         = true
      preload                    = true
      override                   = true
    }
  }
}

How to prevent it from happening again

Fixing one distribution is easy. Stopping the next one from shipping with allow-all is the part that keeps you out of trouble.

Catch it in CI with policy-as-code

If you use Terraform, add a Checkov or OPA gate to your pipeline. Checkov ships with a built-in rule for this:

checkov -d . --check CKV_AWS_174

Or write a Conftest/OPA Rego policy that fails any behavior allowing HTTP:

package cloudfront

deny[msg] {
  resource := input.resource.aws_cloudfront_distribution[name]
  resource.default_cache_behavior[_].viewer_protocol_policy == "allow-all"
  msg := sprintf("Distribution %s allows HTTP on default behavior", [name])
}

deny[msg] {
  resource := input.resource.aws_cloudfront_distribution[name]
  resource.ordered_cache_behavior[_].viewer_protocol_policy == "allow-all"
  msg := sprintf("Distribution %s allows HTTP on an ordered behavior", [name])
}

Wire this into the pull request stage so a non-compliant distribution never reaches apply.

Detect drift at runtime

IaC gates only cover resources created through IaC. Someone with console access can still flip a behavior back to allow-all, and click-ops distributions bypass your pipeline entirely. This is where continuous scanning matters. Lensix runs the cloudfront_http check across every distribution in your accounts on a schedule, so a manual change or an out-of-band deployment gets flagged within the next scan cycle rather than at the next audit.

Note: AWS Config also offers a managed rule, cloudfront-viewer-policy-https, that evaluates the same condition. It is a solid backstop if you already run Config, though it carries per-evaluation cost and only covers accounts where it is deployed.


Best practices

  • Default to redirect-to-https everywhere unless you have a specific reason to reject HTTP outright with https-only. Both are correct, but redirect keeps older clients working.
  • Enforce TLS 1.2 or higher. Set the minimum protocol version on the distribution's viewer certificate config to TLSv1.2_2021 so you are not just encrypting, but encrypting with a modern cipher.
  • Encrypt the origin leg too. Set the origin protocol policy to https-only for custom origins so traffic stays encrypted all the way to your backend, not just to the edge.
  • Add HSTS with preload for public sites so browsers never attempt HTTP after the first visit. Submit your domain to the HSTS preload list once you are confident every subdomain serves HTTPS.
  • Audit every behavior, not just the default. Path-based behaviors are a common blind spot. A distribution can have a secure default and a wide-open /legacy/* behavior that nobody remembers adding.
  • Review distributions when ownership changes. Distributions outlive the teams that create them. Build a periodic review into your security cadence so abandoned distributions do not quietly drift.

Encryption in transit at the edge is one of the cheapest and highest-impact controls you can apply to a public-facing app. The fix takes a few minutes and the protection lasts as long as the distribution does, provided you also gate against regressions. Set the policy, add HSTS, and put a check in your pipeline so it stays that way.