Skip to main content
S3 static website hosting lets you serve HTML, CSS, JavaScript, and other static assets directly from a bucket over HTTP. Configure it with the website variable.
Static website hosting over plain HTTP requires the bucket to allow public read access. You must set block_public_acls = false, block_public_policy = false, ignore_public_acls = false, and restrict_public_buckets = false, and attach an appropriate bucket policy. For HTTPS, use CloudFront in front of the bucket instead.

Index and error documents

module "s3_bucket" {
  source = "terraform-aws-modules/s3-bucket/aws"

  bucket = "my-static-website-bucket"

  website = {
    index_document = "index.html"
    error_document = "error.html"
  }
}

Redirect all requests

To redirect every request to another host — for example when migrating to a new domain — use redirect_all_requests_to. This option conflicts with index_document and error_document.
module "s3_bucket" {
  source = "terraform-aws-modules/s3-bucket/aws"

  bucket = "my-redirect-bucket"

  website = {
    redirect_all_requests_to = {
      host_name = "https://modules.tf"
    }
  }
}
You can also include a protocol key (http or https) to control the scheme of the redirect target.

Routing rules

Routing rules let you conditionally redirect requests based on the key prefix or HTTP error code returned:
module "s3_bucket" {
  source = "terraform-aws-modules/s3-bucket/aws"

  bucket = "my-static-website-bucket"

  website = {
    index_document = "index.html"
    error_document = "error.html"

    routing_rules = [
      {
        condition = {
          key_prefix_equals = "docs/"
        }
        redirect = {
          replace_key_prefix_with = "documents/"
        }
      },
      {
        condition = {
          http_error_code_returned_equals = 404
          key_prefix_equals               = "archive/"
        }
        redirect = {
          host_name          = "archive.myhost.com"
          http_redirect_code = 301
          protocol           = "https"
          replace_key_with   = "not_found.html"
        }
      }
    ]
  }
}

Routing rule fields

Condition block:
FieldDescription
key_prefix_equalsRedirect when the key starts with this prefix
http_error_code_returned_equalsRedirect when S3 returns this HTTP error code
Redirect block:
FieldDescription
host_nameRedirect to this hostname
http_redirect_codeHTTP status code for the redirect (default 301)
protocolhttp or https
replace_key_prefix_withReplace the matching prefix in the key
replace_key_withReplace the entire key

Output values

After applying, use these outputs to wire the bucket into DNS or CloudFront:
output "website_endpoint" {
  value = module.s3_bucket.s3_bucket_website_endpoint
}

output "website_domain" {
  value = module.s3_bucket.s3_bucket_website_domain
}
OutputExample value
s3_bucket_website_endpointmy-bucket.s3-website-eu-west-1.amazonaws.com
s3_bucket_website_domains3-website-eu-west-1.amazonaws.com
The s3_bucket_website_domain value is the Route 53 alias target you need when creating an A ALIAS record.

Making the bucket publicly readable

For a publicly accessible website you need to relax the public access block settings and attach a bucket policy:
module "s3_bucket" {
  source = "terraform-aws-modules/s3-bucket/aws"

  bucket = "my-static-website-bucket"

  block_public_acls       = false
  block_public_policy     = false
  ignore_public_acls      = false
  restrict_public_buckets = false

  attach_policy = true
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid       = "PublicRead"
        Effect    = "Allow"
        Principal = "*"
        Action    = "s3:GetObject"
        Resource  = "_S3_BUCKET_ARN_/*"
      }
    ]
  })

  website = {
    index_document = "index.html"
    error_document = "error.html"
  }
}
For production websites consider using CloudFront with an Origin Access Control (OAC) instead of a public bucket. CloudFront provides HTTPS, caching, and global distribution.