This check flags CloudFront origins set to http-only or match-viewer, which let traffic between CloudFront and your origin travel unencrypted. Switch the origin protocol policy to https-only so edge-to-origin traffic is always encrypted.
CloudFront sits between your users and your backend, terminating TLS at the edge and pulling content from an origin behind the scenes. Most teams obsess over the viewer side of that connection, the part where browsers talk to CloudFront, and forget that there is a second leg: CloudFront talking to your origin. If that second leg runs over plain HTTP, you have an encrypted front door bolted onto an unencrypted hallway.
The cloudfront_insecureorigin check catches exactly that situation. It looks at each origin on your distribution and fails when the origin protocol policy is set to http-only or match-viewer.
What this check detects
Every CloudFront origin has an OriginProtocolPolicy (for custom origins) that controls how CloudFront connects back to your server. It has three possible values:
- https-only — CloudFront always connects to the origin over HTTPS. This is the safe option.
- http-only — CloudFront always connects over plain HTTP. Traffic to the origin is never encrypted.
- match-viewer — CloudFront uses whatever protocol the viewer used. If a user requests over HTTP, CloudFront fetches from the origin over HTTP.
The check fails on http-only and match-viewer. The first is unconditionally insecure. The second is sneakier: it looks fine when everyone uses HTTPS, but the moment a viewer connects over HTTP (or you allow HTTP at the viewer level), the origin connection silently drops to cleartext.
Note: This applies to custom origins, things like an ALB, an EC2 instance, an API Gateway, or any HTTP server you point CloudFront at. S3 origins use Origin Access Control or Origin Access Identity and are handled differently, so this protocol policy does not apply to them.
Why it matters
The whole point of putting CloudFront in front of an app is usually to serve content securely and fast. An insecure origin protocol undermines the secure half of that promise.
The traffic is not as private as you think
People assume that because CloudFront and the origin are both inside AWS, the connection is somehow safe. That assumption does not hold. CloudFront edge locations are spread across the globe, and the connection back to your origin traverses public network paths and shared infrastructure. Anyone able to observe that traffic, whether through a misconfigured network device, a compromised hop, or a man-in-the-middle position, can read every request and response in plaintext.
That includes session cookies, authorization headers, API tokens, form submissions, and personal data. None of it is protected on the origin leg when you run HTTP-only.
match-viewer creates a downgrade path
With match-viewer, an attacker who can force or trick a viewer into using HTTP gets the origin connection downgraded for free. This is a classic protocol downgrade scenario, and it defeats the purpose of HSTS and HTTPS-everywhere policies you may have set elsewhere.
Warning: Even if your viewer protocol policy redirects HTTP to HTTPS, match-viewer on the origin can still produce an HTTP origin fetch during the initial request before the redirect resolves. The two settings are independent, and fixing one does not fix the other.
Compliance and audit exposure
Frameworks like PCI DSS, HIPAA, and SOC 2 expect encryption of data in transit across the full path, not just the first hop. An auditor who finds CloudFront terminating TLS and then talking HTTP to a backend handling cardholder or health data will write that up. "It is internal AWS traffic" is not an accepted control.
How to fix it
The fix is to set the origin protocol policy to https-only. Before you do, make sure your origin actually serves HTTPS on the port CloudFront will use, otherwise CloudFront will start returning 502 errors.
Step 1: Confirm the origin speaks HTTPS
Point a quick request at your origin over HTTPS to confirm it responds with a valid certificate.
curl -vI https://origin.example.com/healthz
If your origin is an ALB, attach an ACM certificate and add an HTTPS listener on 443. If it is an EC2 instance or a self-managed server, install a certificate that CloudFront will trust (a public CA cert, not a self-signed one, unless you configure the origin SSL settings accordingly).
Step 2: Find the distribution and current config
aws cloudfront list-distributions \
--query "DistributionList.Items[].{Id:Id,Domain:DomainName}" \
--output table
Pull the current config and the ETag, which you need to submit an update.
aws cloudfront get-distribution-config \
--id E1ABCDEF234567 \
--output json > dist-config.json
Step 3: Edit the origin protocol policy
In the saved file, find the origin under DistributionConfig.Origins.Items and update its CustomOriginConfig:
"CustomOriginConfig": {
"HTTPPort": 80,
"HTTPSPort": 443,
"OriginProtocolPolicy": "https-only",
"OriginSslProtocols": {
"Quantity": 2,
"Items": ["TLSv1.2", "TLSv1.3"]
}
}
While you are here, drop older TLS versions from OriginSslProtocols. There is rarely a reason to keep TLSv1 or TLSv1.1 on the origin handshake.
Step 4: Apply the update
The update-distribution call wants the inner DistributionConfig object and the ETag from the get call. A small jq step makes this painless.
Danger: This updates a live distribution. If the origin does not actually serve HTTPS on port 443 with a trusted certificate, CloudFront will immediately start returning 502 Bad Gateway to your users. Validate Step 1 before running this.
ETAG=$(jq -r '.ETag' dist-config.json)
jq '.DistributionConfig' dist-config.json > dist-config-inner.json
aws cloudfront update-distribution \
--id E1ABCDEF234567 \
--distribution-config file://dist-config-inner.json \
--if-match "$ETAG"
The distribution status will show InProgress while the change propagates to edge locations. This usually completes in a few minutes.
Terraform fix
If you manage CloudFront with Terraform, the relevant block is custom_origin_config:
resource "aws_cloudfront_distribution" "app" {
# ... other config ...
origin {
domain_name = "origin.example.com"
origin_id = "app-origin"
custom_origin_config {
http_port = 80
https_port = 443
origin_protocol_policy = "https-only"
origin_ssl_protocols = ["TLSv1.2", "TLSv1.3"]
}
}
}
Tip: Pair the origin fix with a viewer-side policy of redirect-to-https or https-only in your cache behaviors. That closes both legs of the connection in one pass and means no request ever rides cleartext end to end.
How to prevent it from happening again
Fixing one distribution is easy. Keeping every distribution compliant as your team ships new ones is the real work. Put a guardrail in the pipeline so an insecure origin never reaches production.
Block it in Terraform with a policy-as-code check
Here is an OPA/Rego rule for Conftest that fails any plan introducing an insecure origin protocol:
package main
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_cloudfront_distribution"
origin := resource.change.after.origin[_]
policy := origin.custom_origin_config[_].origin_protocol_policy
policy != "https-only"
msg := sprintf("CloudFront origin '%s' uses insecure protocol '%s'", [origin.origin_id, policy])
}
Wire it into CI against a plan output:
terraform plan -out=tfplan.bin
terraform show -json tfplan.bin > tfplan.json
conftest test tfplan.json
Catch existing drift continuously
Policy-as-code only sees what flows through your pipeline. Resources created by hand, by other teams, or by a console click still slip through. That is where a continuous check like Lensix earns its keep: it scans your live AWS accounts on a schedule and flags any distribution whose origins use http-only or match-viewer, including ones that were never in your Terraform state.
Tip: Run a pipeline gate and a continuous scanner. The gate stops new mistakes at merge time, and the scanner catches everything that was created outside the pipeline or drifted afterward.
Best practices
- Default to https-only on every custom origin. Treat
match-vieweras a misconfiguration, not a convenience. It almost never does what people assume. - Encrypt both legs. Set viewer cache behaviors to
redirect-to-httpsand origins tohttps-onlyso no request is ever cleartext end to end. - Restrict origin SSL protocols to TLS 1.2 and above. Drop TLSv1 and TLSv1.1 from
origin_ssl_protocolsto avoid weak handshakes on the backend connection. - Lock down the origin so it only accepts CloudFront. Add a custom header that CloudFront sends and your origin requires, or use the AWS-managed CloudFront prefix list in your security groups. HTTPS protects the data, but origin restriction stops people from bypassing CloudFront entirely.
- Use a real certificate on the origin. Self-signed certs force you to loosen origin SSL settings and weaken validation. A public CA cert, easily issued through ACM on an ALB, keeps the handshake strict.
- Codify it. Bake the secure origin config into a reusable Terraform module so every new distribution inherits the right defaults instead of starting from scratch.
The origin connection is the part of a CloudFront setup people forget about precisely because it is invisible. Make it encrypted once, gate it in CI, and scan for drift, and you remove an entire class of quiet data-in-transit exposure.

