Yeah, that 403 Bad Request from CloudFront is obnoxious—especially when you've triple-checked your bucket permissions. Let's cut through the noise.
The Fix: Check Your Origin Response Headers
The most common culprit behind this specific error is a custom origin response header that CloudFront doesn't like. Head to your distribution in the AWS Console:
- Open CloudFront → select your distribution → Origins tab.
- Edit your origin.
- Under Origin custom headers, look for any header with a value that contains a newline or weird character—like a trailing space or a line break. CloudFront will reject those with a 403 Bad Request.
- Remove or fix those headers. Save and wait for the distribution to deploy (takes 5–10 minutes).
I've seen this happen twice in production. Once, someone had pasted an API key from a config file that accidentally included a newline. Another time, a variable in a CI/CD script had a trailing space. Both times, CloudFront gave the vague 403 Bad Request instead of telling us which header was broken.
Why This Works
CloudFront validates custom origin headers before forwarding requests. Any header value with invalid characters—including spaces that shouldn't be there—breaks the request. The error message you see is generic because CloudFront doesn't want to leak header details to the client. That's why it says "The request could not be satisfied. Bad request." without specifics.
Once those headers are clean, CloudFront can forward the request to your origin without choking.
Less Common Variations
1. S3 Origin with a Signed URL
If you're using S3 as your origin and have CloudFront signed URLs enabled, make sure you're actually signing the request. I've seen people enable signed URLs in the distribution but forget to sign the URL in their app. That gives you a 403, but often says Access Denied—however, I've also seen it surface as a generic 403 Bad Request when the signature is malformed.
Check your URL structure:
https://yourapp.cloudfront.net/path?Expires=...&Signature=...&Key-Pair-Id=...
If any of those parameters are missing or have extra characters, fix them.
2. Lambda@Edge or CloudFront Functions Modifying the Request
You've got a viewer request function? If it's adding or modifying headers, it could be injecting bad values. I debugged one where a CloudFront Function was adding a large header value, and CloudFront's 4KB limit on headers got exceeded. That gave a 403 Bad Request on every request.
Disable your functions temporarily to test. If the error disappears, your function is the problem. Look for header size limits and invalid characters in the function code.
3. Origin with Custom Port or Protocol
If your origin is a custom HTTP server (like an EC2 instance) and you set the origin protocol policy to HTTPS only, but your origin is actually listening on HTTP, CloudFront will try HTTPS and fail with a 403 Bad Request. Check the origin's protocol setting and match it to what your server actually supports.
4. Cache Behavior with Compressed Content
If you've got a cache behavior that compresses objects (like gzip), and your origin sends a Content-Encoding header that CloudFront doesn't understand, it might break. Test by disabling compression temporarily. If the 403 goes away, adjust your origin's encoding headers.
Prevention Tips
You don't want to catch this at 2 AM on a Friday. Here's how to keep it from coming back:
- Validate headers in your CI/CD — before you deploy, run a quick script that checks all custom headers for newlines or trailing spaces. I use
grepfor that:
grep -rn $'\r' --include='*.json' --include='*.env' .
That catches carriage returns in header values.
- Use CloudFront Functions for header management — instead of manually setting headers on the origin, normalize them in a viewer request function. That way, you control the exact values CloudFront sees.
- Log your origin requests — enable CloudFront access logging. When you see a 403, you can grep the logs for the actual request details and spot the bad header faster.
- Test with curl after every change — a simple
curl -vagainst your CloudFront URL shows you exactly what's being sent and received. Do that before you close out a ticket.
One last thing—if you're using a custom origin, make sure your web server isn't returning a 403 itself. CloudFront passes that through as a 403 to the client, but the response body will often include your server's error page. If you see that, the problem is on your origin, not CloudFront. That's a whole different fix, but at least you'll know where to look.
Hope that clears it up. You're welcome to ping me if you get stuck on a specific step.