Post

Static Site Hosting with Azure Storage and AWS S3

Introduction to Static Site Hosting on Cloud Storage

Static site hosting has become increasingly popular for deploying websites that don’t require server-side processing. Both Azure Blob Storage and AWS S3 provide cost-effective, scalable solutions for hosting static websites directly from cloud storage. This approach eliminates the need for traditional web servers, reduces operational costs, and provides excellent performance through global content delivery networks (CDNs).

In this post, we’ll explore how to use Azure Blob Storage and AWS S3 to host static websites, compare their features, and discuss best practices for deployment.

What is Static Site Hosting?

Static site hosting serves HTML, CSS, JavaScript, and other static assets directly to users without server-side processing. This is ideal for:

  • Single Page Applications (SPAs) built with React, Vue, or Angular
  • Documentation sites
  • Personal blogs and portfolios
  • Marketing and landing pages
  • JAMstack applications

Benefits of Cloud Storage for Static Sites

  • Cost-Effective: Pay only for storage and bandwidth used
  • Scalability: Automatically handles traffic spikes
  • High Availability: Built-in redundancy and durability
  • Global Distribution: Easy integration with CDNs
  • Simple Deployment: No server management required
  • HTTPS Support: Built-in SSL/TLS certificates

Hosting Static Sites on Azure Blob Storage

Azure Blob Storage provides a dedicated static website hosting feature that makes it easy to serve static content directly from a storage container.

Prerequisites

  • Azure subscription
  • Azure CLI installed or Azure Portal access
  • A static website ready to deploy

Step 1: Create a Storage Account

Using Azure CLI:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Set variables
RESOURCE_GROUP="my-static-site-rg"
LOCATION="eastus"
STORAGE_ACCOUNT_NAME="mystaticsite$(date +%s)"

# Create resource group
az group create \
  --name $RESOURCE_GROUP \
  --location $LOCATION

# Create storage account
az storage account create \
  --name $STORAGE_ACCOUNT_NAME \
  --resource-group $RESOURCE_GROUP \
  --location $LOCATION \
  --sku Standard_LRS \
  --kind StorageV2

Step 2: Enable Static Website Hosting

Enable the static website feature on your storage account:

1
2
3
4
5
6
# Enable static website hosting
az storage blob service-properties update \
  --account-name $STORAGE_ACCOUNT_NAME \
  --static-website \
  --404-document 404.html \
  --index-document index.html

This creates a special container named $web that serves as the root of your static website.

Step 3: Get the Website URL

Retrieve the primary endpoint for your static website:

1
2
3
4
5
6
# Get the static website URL
az storage account show \
  --name $STORAGE_ACCOUNT_NAME \
  --resource-group $RESOURCE_GROUP \
  --query "primaryEndpoints.web" \
  --output tsv

The URL will look like: https://mystaticsite.z13.web.core.windows.net/

Step 4: Upload Your Static Content

Upload your website files to the $web container:

1
2
3
4
5
6
# Upload files
az storage blob upload-batch \
  --account-name $STORAGE_ACCOUNT_NAME \
  --source ./dist \
  --destination '$web' \
  --overwrite

For individual file uploads:

1
2
3
4
5
6
7
# Upload index.html
az storage blob upload \
  --account-name $STORAGE_ACCOUNT_NAME \
  --container-name '$web' \
  --name index.html \
  --file ./index.html \
  --content-type "text/html"

Step 5: Configure Custom Domain (Optional)

To use a custom domain:

  1. Add a CNAME record in your DNS provider pointing to the storage account endpoint
  2. Map the custom domain in Azure:
1
2
3
4
az storage account update \
  --name $STORAGE_ACCOUNT_NAME \
  --resource-group $RESOURCE_GROUP \
  --custom-domain www.yourdomain.com

Step 6: Enable CDN for Better Performance

For improved performance and HTTPS support with custom domains:

1
2
3
4
5
6
7
8
9
10
11
12
13
# Create CDN profile
az cdn profile create \
  --name my-cdn-profile \
  --resource-group $RESOURCE_GROUP \
  --sku Standard_Microsoft

# Create CDN endpoint
az cdn endpoint create \
  --name my-static-site \
  --profile-name my-cdn-profile \
  --resource-group $RESOURCE_GROUP \
  --origin $STORAGE_ACCOUNT_NAME.z13.web.core.windows.net \
  --origin-host-header $STORAGE_ACCOUNT_NAME.z13.web.core.windows.net

Hosting Static Sites on AWS S3

AWS S3 (Simple Storage Service) has been offering static website hosting for years and provides a robust, scalable solution.

Prerequisites

  • AWS account
  • AWS CLI configured with credentials
  • A static website ready to deploy

Step 1: Create an S3 Bucket

Create a bucket with a name matching your domain (if using custom domain):

1
2
3
4
5
6
# Set variables
BUCKET_NAME="my-static-website.com"
REGION="us-east-1"

# Create bucket
aws s3 mb s3://$BUCKET_NAME --region $REGION

Step 2: Enable Static Website Hosting

Configure the bucket for static website hosting:

1
2
3
4
# Enable static website hosting
aws s3 website s3://$BUCKET_NAME/ \
  --index-document index.html \
  --error-document 404.html

Step 3: Configure Bucket Policy for Public Access

Create a bucket policy to allow public read access:

Create a file named bucket-policy.json:

1
2
3
4
5
6
7
8
9
10
11
12
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-static-website.com/*"
    }
  ]
}

Apply the policy:

1
2
3
4
5
6
7
8
9
10
# Update bucket policy
aws s3api put-bucket-policy \
  --bucket $BUCKET_NAME \
  --policy file://bucket-policy.json

# Disable block public access
aws s3api put-public-access-block \
  --bucket $BUCKET_NAME \
  --public-access-block-configuration \
  "BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false"

Step 4: Upload Your Static Content

Upload your website files:

1
2
3
4
5
6
7
# Upload entire directory
aws s3 sync ./dist s3://$BUCKET_NAME --delete

# Upload with cache control
aws s3 sync ./dist s3://$BUCKET_NAME \
  --cache-control "max-age=3600" \
  --delete

For individual files:

1
2
3
4
# Upload single file
aws s3 cp index.html s3://$BUCKET_NAME/ \
  --content-type "text/html" \
  --cache-control "max-age=3600"

Step 5: Get the Website URL

The website endpoint will be:

1
http://{bucket-name}.s3-website-{region}.amazonaws.com

For example:

1
http://my-static-website.com.s3-website-us-east-1.amazonaws.com

Step 6: Configure CloudFront for HTTPS and Custom Domain

For HTTPS support and better performance, set up CloudFront:

1
2
3
4
# Create CloudFront distribution
aws cloudfront create-distribution \
  --origin-domain-name $BUCKET_NAME.s3-website-$REGION.amazonaws.com \
  --default-root-object index.html

Or using a configuration file cloudfront-config.json:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
{
  "CallerReference": "my-static-site-1",
  "Comment": "Static website distribution",
  "Enabled": true,
  "DefaultRootObject": "index.html",
  "Origins": {
    "Quantity": 1,
    "Items": [
      {
        "Id": "S3-my-static-website",
        "DomainName": "my-static-website.com.s3-website-us-east-1.amazonaws.com",
        "CustomOriginConfig": {
          "HTTPPort": 80,
          "HTTPSPort": 443,
          "OriginProtocolPolicy": "http-only"
        }
      }
    ]
  },
  "DefaultCacheBehavior": {
    "TargetOriginId": "S3-my-static-website",
    "ViewerProtocolPolicy": "redirect-to-https",
    "AllowedMethods": {
      "Quantity": 2,
      "Items": ["GET", "HEAD"]
    },
    "ForwardedValues": {
      "QueryString": false,
      "Cookies": {
        "Forward": "none"
      }
    },
    "MinTTL": 0
  }
}

Comparison: Azure Blob Storage vs AWS S3

FeatureAzure Blob StorageAWS S3
Pricing~$0.0184/GB/month~$0.023/GB/month
CDN IntegrationAzure CDNCloudFront
Custom Domain HTTPSRequires Azure CDNRequires CloudFront
Default IndexSupports index.htmlSupports index.html
Error PagesSingle 404 pageSingle error page
Directory IndexNot supportedNot supported
Setup ComplexityLowLow
CLI ToolsAzure CLIAWS CLI
SPA RoutingRequires CDN rulesRequires CloudFront functions

Best Practices for Static Site Hosting

1. Use a CDN

Always use a CDN (Azure CDN or CloudFront) for production sites:

  • Improved global performance
  • HTTPS support for custom domains
  • DDoS protection
  • Better caching control

2. Set Proper Cache Headers

Configure appropriate cache headers for different file types:

1
2
3
4
5
6
7
8
9
10
# Azure: Upload with cache control
az storage blob upload \
  --container-name '$web' \
  --file style.css \
  --name style.css \
  --content-cache-control "public, max-age=31536000, immutable"

# AWS: Upload with cache control
aws s3 cp style.css s3://$BUCKET_NAME/ \
  --cache-control "public, max-age=31536000, immutable"

3. Implement CI/CD

Automate deployments using GitHub Actions, Azure DevOps, or AWS CodePipeline:

Example GitHub Action for Azure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
name: Deploy to Azure Storage

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      
      - name: Build
        run: npm run build
      
      - name: Deploy to Azure
        uses: azure/CLI@v1
        with:
          inlineScript: |
            az storage blob upload-batch \
              --account-name $ \
              --source ./dist \
              --destination '$web' \
              --overwrite

Example GitHub Action for AWS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
name: Deploy to S3

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      
      - name: Build
        run: npm run build
      
      - name: Deploy to S3
        env:
          AWS_ACCESS_KEY_ID: $
          AWS_SECRET_ACCESS_KEY: $
        run: |
          aws s3 sync ./dist s3://$ --delete

4. Handle SPA Routing

For Single Page Applications with client-side routing, configure your CDN to redirect all routes to index.html:

Azure CDN: Create URL rewrite rules CloudFront: Use CloudFront Functions or Lambda@Edge

5. Enable Compression

Enable gzip/brotli compression:

1
2
3
4
5
6
7
8
9
# Azure: Upload pre-compressed files
az storage blob upload \
  --file styles.css.gz \
  --name styles.css \
  --content-encoding gzip

# AWS: Upload pre-compressed files
aws s3 cp styles.css.gz s3://$BUCKET_NAME/styles.css \
  --content-encoding gzip

6. Security Considerations

  • Use HTTPS for all production sites
  • Implement Content Security Policy (CSP) headers
  • Set appropriate CORS policies
  • Regularly update dependencies
  • Monitor access logs
  • Use IAM roles with minimal permissions

7. Cost Optimization

  • Set lifecycle policies to delete old versions
  • Use appropriate storage tiers
  • Monitor bandwidth usage
  • Implement proper caching to reduce origin requests
  • Clean up unused resources

Conclusion

Both Azure Blob Storage and AWS S3 provide excellent platforms for hosting static websites. The choice between them often depends on:

  • Your existing cloud infrastructure
  • Specific feature requirements
  • Regional availability
  • Cost considerations
  • Team expertise

For most use cases, both platforms offer comparable performance and features. Azure Blob Storage might be preferable if you’re already using Azure services, while AWS S3 is a great choice if you’re in the AWS ecosystem.

Key takeaways:

  • Static site hosting on cloud storage is cost-effective and scalable
  • Always use a CDN for production deployments
  • Implement proper caching strategies
  • Automate deployments with CI/CD
  • Follow security best practices

By leveraging cloud storage for static site hosting, you can build and deploy modern web applications with minimal operational overhead while maintaining excellent performance and reliability.

Additional Resources

This post is licensed under CC BY 4.0 by the author.