Static Web Hosting with Ceph S3
With Ceph's S3-compatible Object Storage, you have the ability to host static websites simply, with high availability, and in a resource-efficient manner. Instead of running a traditional web server (such as Apache or NGINX), you can store your HTML, CSS, and JavaScript files as well as images directly in an S3 bucket and make them accessible over the Internet.
This type of hosting is ideal for:
- Static websites and blogs (e.g., generated with Hugo, Jekyll, or MkDocs)
- Single-Page Applications (SPAs) (e.g., React, Vue.js, Angular)
- Documentation, portfolios, and online catalogs
Benefits of S3 Web Hosting
- No server management: There is no need to update operating systems or configure and maintain web servers.
- High scalability and performance: Ceph handles data distribution and replication in the background. Your website remains performant and accessible even during sudden traffic spikes.
- Cost-effective: The overhead for running complete virtual machines is eliminated; only the actual object storage is used.
In the following sections, you will learn how to prepare a bucket for web hosting, upload your website files, and set the correct access permissions (Bucket Policies) so that your site becomes publicly accessible.
How Delivery Works
The following diagram illustrates how visitor requests are answered directly by the Ceph Object Gateway, without the need for a dedicated web server (such as Apache/NGINX) in between:
graph LR
Client([Visitor / Web Browser]) -- "GET Request\nhttps://my-bucket.sites..." --> Gateway{Ceph S3 Gateway}
Gateway --> Bucket[(S3 Bucket)]
Bucket -. "index.html,\nImages, CSS, JS" .-> Gateway
Gateway -. "Delivers website" .-> Client
Requirements
For your website to be successfully served via Ceph S3, several prerequisites must be met:
Bucket Name
- Uniqueness: The name of your S3 bucket must be globally unique, as the access URLs are based on the bucket name (e.g.,
s3://my-bucket). - Rules: Follow the standard naming conventions for S3 buckets (e.g., lowercase letters, hyphens, no special characters or spaces).
Website Content
- Static files: You need at least one
index.htmlfile. This is the default landing page that is loaded when a user visits the website URL. - Optional error page: It is recommended to provide an
error.htmlfile. This is displayed when a user tries to access a page or file that does not exist. - File structure: Keep your website structure clean. Complex subfolder structures work but require precise path references in your HTML file links.
Access Permissions
- Public read access: The bucket must be configured so that anonymous users (anyone on the Internet) can read the files.
- No write access: Ensure that only authorized users have write permissions to edit the content.
- Bucket Policy: You must create a bucket policy that explicitly allows public access to the objects (files) in the bucket.
Once these prerequisites are met, you can proceed with the configuration.
Tutorial
Step 1: Install and Prepare the AWS CLI
To interact with S3 storage, we need the AWS CLI.
Tip: A detailed guide for the recommended installation of the AWS CLI (via Python venv) as well as information about advanced IAM features can be found in our separate documentation: AWS CLI & IAM Features.
If you have already installed the AWS CLI in a virtual environment, make sure it is activated for the next steps:
source ~/ceph.venv/bin/activate
Step 2: Test S3 Endpoints (Optional)
You can verify in advance that the website endpoints are reachable:
curl http://sites.ewstorage.ch
curl https://sites.ewstorage.ch
Step 3: Configure AWS CLI and Set Environment Variables
Set the required credentials and configuration parameters as environment variables. Replace the xxx for AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY with your actual S3 credentials.
Also adjust the bucket name to match your requirements.
# Credentials and settings
export AWS_ACCESS_KEY_ID=xxx
export AWS_SECRET_ACCESS_KEY=xxx
export AWS_REQUEST_CHECKSUM_CALCULATION=when_required
export AWS_RESPONSE_CHECKSUM_VALIDATION=when_required
export AWS_MAX_ATTEMPTS=1
# Regional parameters and bucket name (Here region 1: ch-zh1)
export url=https://s3.ewstorage.ch
export region=ch-zh1
export bucket=my-website-bucket
Step 4: Create Bucket
Now we create the S3 bucket that will later function as the web server:
# List existing buckets
aws s3 ls
# Create new bucket
aws s3 mb s3://${bucket}
# Verify the bucket was created
aws s3 ls s3://${bucket}
[PLACEHOLDER: A screenshot of the console output when creating the bucket could be inserted here]
Step 5: Set Bucket Policy (Public Read Access)
For visitors to view your website, the files in the bucket must be publicly readable. To achieve this, we assign a policy to the bucket:
aws s3api put-bucket-policy --bucket="${bucket}" --policy '
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::'${bucket}'/*"
}
]
}'
# Query the policy for verification (requires jq)
aws s3api get-bucket-policy --bucket="${bucket}" --output text | jq .
Step 6: Enable Website Hosting on Bucket
This command instructs the bucket to behave like a web server and look for an index.html file by default.
aws s3api put-bucket-website \
--bucket=${bucket} \
--website-configuration='{"IndexDocument":{"Suffix":"index.html"}}'
Step 7: Create and Upload Test Website (index.html)
We create a simple index.html and upload it directly to the bucket:
echo '<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test Page</title>
</head>
<body>
<h1>Hello, World</h1>
<p>This is a simple test file for index.html.</p>
</body>
</html>' | aws s3 cp - s3://${bucket}/index.html
[PLACEHOLDER: A screenshot of the upload process could be inserted here]
Step 8: Access and Test the Website
Your static website is now live! You can access it via the specific subdomain of the bucket.
# Access the root directory (automatically loads index.html)
curl https://${bucket}.sites.ewstorage.ch/
# Direct access to index.html
curl https://${bucket}.sites.ewstorage.ch/index.html
# List bucket contents
aws s3 ls s3://${bucket}
[PLACEHOLDER: A screenshot of the working website in the browser could be inserted here]
Endpoints for Other Regions (e.g., Region 2: ch-ge1)
If you want to host your bucket in a different region (such as ch-ge1), adjust the URL and region accordingly:
export url=https://s3.ch-ge1.ewstorage.ch
export region=ch-ge1
# Test access for the second region
curl https://${bucket}.sites.ch-ge1.ewstorage.ch/index.html
Advanced Configurations
Setting Up CORS (Cross-Origin Resource Sharing)
What is CORS?
CORS is a security mechanism in modern web browsers. By default, it prevents a website (e.g., https://www.my-website.com) from accessing resources from a different domain (e.g., https://my-bucket.sites.ewstorage.ch) via JavaScript or stylesheets.
When do you need CORS in S3?
If you use your S3 bucket to host web fonts (.woff, .ttf), JavaScript modules, or JSON data (APIs) that you then want to embed on a different domain, the browser will block the loading process for security reasons. In the browser's developer tools, you will then see the typical "CORS error".
To inform the S3 bucket that this access is explicitly allowed, you must define a CORS policy for the bucket.
Setting CORS Policy via AWS CLI
We create a simple configuration file (cors.json) that allows read operations (GET, HEAD) from all domains (*), and assign it to the bucket.
# 1. Create configuration file cors.json
cat <<EOF > cors.json
{
"CORSRules": [
{
"AllowedOrigins": ["*"],
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "HEAD"],
"MaxAgeSeconds": 3000
}
]
}
EOF
# 2. Apply CORS policy to the bucket
aws s3api put-bucket-cors \
--bucket ${bucket} \
--cors-configuration file://cors.json
# 3. Query for verification
aws s3api get-bucket-cors --bucket ${bucket}
Security Tip: If you know exactly which domain your website runs on (e.g.,
https://www.my-website.com), you should replace the wildcard"*"in thecors.jsonfile under"AllowedOrigins"with your specific domain (e.g.,["https://www.my-website.com"]) to enhance security.