Secure Static Content Hosting with S3, CloudFront, and Basic Authentication
This guide demonstrates how to:
- Host static assets in an AWS S3 bucket
- Serve them globally via CloudFront with an Origin Access Control (OAC)
- Enforce Basic Authentication at edge locations using Lambda@Edge
- Secure access through precise IAM policies
Step 1: Create and Secure the S3 Bucket
1.1 Create a New S3 Bucket
- Console: S3 > Create bucket.
- Bucket name:
your-docs-bucket(must be globally unique). - Region:
us-east-1(required for Lambda@Edge). - Uncheck "Block all public access." We lock it down with CloudFront instead.
1.2 Configure Bucket Policy
Allow only CloudFront to fetch objects. Replace placeholders with actual IDs:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "cloudfront.amazonaws.com" },
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::your-docs-bucket/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/EDFDVBD632BHDS5"
}
}
}
]
}
Step 2: Set Up CloudFront Distribution
2.1 Create a Distribution
- Console: CloudFront > Create distribution.
- Origin domain:
your-docs-bucket.s3.us-east-1.amazonaws.com - Origin access: Origin Access Control (OAC) -- create a new OAC.
- Viewer protocol policy: Redirect HTTP to HTTPS.
- Default root object:
index.html. - Alternate domain name (CNAME):
docs.example.com(if using a custom domain).
2.2 Configure Origin Access Control (OAC)
- After distribution creation, go to the Origins tab.
- Select the origin > Edit > Origin access > Apply the new OAC.
- This replaces legacy Origin Access Identity (OAI).
CloudFront will update the bucket policy OAC principal automatically if the option is selected.
Step 3: Implement Basic Authentication with Lambda@Edge
3.1 Create IAM Role for Lambda@Edge
The Lambda function needs a role that trusts both Lambda and CloudFront:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": ["lambda.amazonaws.com", "edgelambda.amazonaws.com"] },
"Action": "sts:AssumeRole"
}
]
}
Name: lambda-edge-basic-auth-role
3.2 Attach Execution Policy
Grant CloudWatch Logs access for debugging:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"],
"Resource": "arn:aws:logs:*:*:*"
}
]
}
3.3 Write the Lambda Function
- Console: Lambda > Create function > Author from scratch.
- Name:
basic-auth-edge, Runtime: Node.js 18.x, Region:us-east-1. - Attach Role:
lambda-edge-basic-auth-role.
Use this code:
'use strict';
exports.handler = async (event) => {
const request = event.Records[0].cf.request;
const headers = request.headers;
const authUser = 'admin';
const authPass = 'P@ssw0rd!';
const authString = 'Basic ' + Buffer.from(authUser + ':' + authPass).toString('base64');
if (
typeof headers.authorization === 'undefined' ||
headers.authorization[0].value !== authString
) {
return {
status: '401',
statusDescription: 'Unauthorized',
body: 'Unauthorized',
headers: {
'www-authenticate': [{ key: 'WWW-Authenticate', value: 'Basic realm="Protected"' }],
},
};
}
return request;
};
3.4 Publish and Version Lambda
- Publish a new version from the Lambda console.
- Note the version ARN:
arn:aws:lambda:us-east-1:123456789012:function:basic-auth-edge:1.
3.5 Attach Lambda@Edge to CloudFront
- CloudFront > Distributions > Edit behavior > Function associations.
- Select Viewer Request and paste the version ARN.
- Save and deploy. Edge locations will propagate in minutes.
Step 4: Final Verification
- Visit
https://docs.example.com. A Basic Auth prompt should appear. - Enter credentials to proceed.
- Attempt direct S3 URL access (
https://your-docs-bucket.s3.amazonaws.com/index.html) -- it should return Access Denied.
Final Notes
The setup is now:
- Scalable S3 storage with no direct public access
- Global CloudFront CDN with HTTPS
- Edge-level Basic Authentication via Lambda@Edge
Security Tip: Rotate Basic Auth credentials periodically and monitor CloudFront logs (S3 bucket logs or Lambda@Edge logs) for unauthorized attempts. For production use cases with more complex authentication requirements, consider integrating with Cognito or an external identity provider instead of Basic Auth.
Need help with this?
Our team handles this kind of work daily. Let us take care of your infrastructure.
Related Articles
The Ultimate Guide to Linux Server Management in 2025
A comprehensive guide to modern Linux server management covering automation, containerization, cloud integration, AI-driven operations, security best practices, and essential tooling for 2025.
Server & DevOpsFixing "421 Misdirected Request" for Plesk Sites on Ubuntu 22.04 After Apache Update
Resolve the 421 Misdirected Request error affecting all HTTPS sites on Plesk for Ubuntu 22.04 after an Apache update, caused by changed SNI requirements in the nginx-to-Apache proxy chain.
Server & DevOpsHow to Set Up GlusterFS on Ubuntu
A complete guide to setting up a distributed, replicated GlusterFS filesystem across multiple Ubuntu 22.04 nodes, including installation, volume creation, client mounting, maintenance, and troubleshooting.