Right, so you’ve got your CloudFront distribution set up. It’s serving your site, caching your assets, and generally feeling pretty snappy. Now, let’s talk about how to not get pwned. Because a fast website that’s also a gaping security hole is just a liability on amphetamines. We’re going to lock this down properly, and I’ll explain the why behind each step so you’re not just cargo-culting configs.

HTTPS: No Exceptions, No Negotiation

This isn’t 2012. HTTPS is not an optional nice-to-have; it’s the absolute bare minimum. The internet is a sketchy alleyway, and HTTP is shouting your credit card details down it. CloudFront makes this stupidly easy to enforce.

You’ll configure this in your Cache Behavior settings. The Viewer Protocol Policy is what you care about. You have three choices, but only one correct one:

  • Allow HTTP and HTTPS: Don’t. Just don’t.
  • Redirect HTTP to HTTPS: This is the correct choice for 99.9% of use cases. It gently guides any idiot (or legacy system) trying to use HTTP over to the secure version.
  • HTTPS Only: This is more aggressive, outright rejecting HTTP requests. It’s technically more secure, but the user experience of a connection error is worse than a seamless redirect.

The other key setting here is the Origin Protocol Policy. Since the viewer-to-CloudFront connection is now HTTPS, you should also force HTTPS on the CloudFront-to-origin leg. Set this to HTTPS Only or Match Viewer (which will use HTTPS since the viewer request is now HTTPS). This ensures your entire pipeline is encrypted, end-to-end.

Integrating AWS WAF: Your Intelligent Bouncer

CloudFront is a delivery network, not a security expert. It will happily serve malicious requests to your origin just as fast as legitimate ones. This is where AWS WAF (Web Application Firewall) comes in. Think of it as your intelligent, configurable bouncer standing in front of your distribution.

You don’t configure WAF in CloudFront; you associate a WAF Web ACL (Access Control List) with your distribution. This is a crucial distinction. The rules live in WAF, and CloudFront just points to them.

Why is this powerful? Reusability. You can create a single Web ACL with a set of rules (e.g., block common SQL injection patterns, rate-limit abusive IPs, block known bad bots) and attach it to multiple CloudFront distributions. It’s a central security policy.

Here’s a quick example of creating a Web ACL and associating it with a distribution using the AWS CLI. First, create the ACL (this is a simplistic example—you’d normally use managed rule groups from AWS Marketplace for the heavy lifting):

# Create a Web ACL. Note the REGION! WAF is regional, but protects global resources.
aws wafv2 create-web-acl \
    --name MyCloudFrontSecurityACL \
    --scope CLOUDFRONT \
    --default-action Allow={} \
    --visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=MyCloudFrontSecurityACL,SampledRequestsEnabled=true \
    --region us-east-1 # WAF for CloudFront MUST be created in us-east-1

Then, associate it with your existing CloudFront distribution by updating the distribution config and specifying the Web ACL ARN under the WebACLId field.

The real art is in crafting the WAF rules themselves. A best practice is to start with the core managed rule sets from AWS (like the AWSManagedRulesCommonRuleSet) and then layer on your own custom rules for your specific application logic.

Field-Level Encryption: Because Some Data is Extra Sensitive

Okay, this one is for the paranoids (and frankly, more people should be paranoid). Standard HTTPS (TLS) encrypts your data in transit. It’s secure between the user and CloudFront and between CloudFront and your origin. But at the edge location, CloudFront needs to be able to read the request to cache things, run functions, etc. So, for a nanosecond, your super-secret data (like a credit card number) is unencrypted in the edge server’s memory.

Field-Level Encryption (FLE) is for when that’s not good enough. It allows you to encrypt specific sensitive fields in the client’s browser using a public key you provide. The data is sent to CloudFront already encrypted. The edge location never sees the plain-text value—it just passes the encrypted blob through to your origin server, which alone holds the private key to decrypt it.

It’s a bit more work, but for truly sensitive data, it’s genius. You define a profile in CloudFront that specifies which fields to encrypt (e.g., payment-form.credit-card-number).

// Example of a CloudFront FLE profile (defined in the AWS CLI or SDK)
{
  "ProviderId": "my-key-provider",
  "PublicKey": "ABC123...", // The public key from your key pair
  "FieldPatterns": {
    "Items": ["payment-form*"] // Encrypts all fields matching this pattern
  }
}

The client-side JavaScript code would then use this public key to encrypt the data before the form is submitted. The origin receives the encrypted value and decrypts it. The beautiful part? Even if an attacker somehow got a memory dump from a CloudFront edge server, your sensitive data would still be safe. It’s a fantastic defense-in-depth measure for specific use cases.

The common pitfall? Complexity. You are now managing cryptographic key pairs and changing your application logic. It’s not for every field, just the crown jewels. But when you need it, nothing else comes close.