Skip to content

S3 buckets should have static website hosting disabled

When you enable static website hosting on an S3 bucket, AWS creates a public HTTP endpoint (e.g., <bucket>.s3-website-<region>.amazonaws.com) that serves objects directly. This endpoint does not support HTTPS, and making it functional usually requires granting public read access through bucket policies or ACLs. Even if the original intent was to serve a few static assets, the entire bucket becomes a target for enumeration.

CloudFront with an origin access control is the right approach for serving static content from S3. It keeps the bucket private, supports HTTPS, and gives you caching and WAF integration without exposing raw S3 endpoints.

Retrofit consideration

Buckets currently serving static websites need migration to CloudFront with origin access control before the website configuration can be removed. Removing the configuration immediately breaks any DNS or application references pointing to the S3 website endpoint.

Implementation

Choose the approach that matches how you manage Terraform.

If you use terraform-aws-modules/s3-bucket/aws, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.

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

  bucket = "abc123"
}

Use AWS provider resources directly. See docs for the resources involved: aws_s3_bucket_website_configuration.

resource "aws_s3_bucket" "this" {
  bucket        = "pofix-abc123"
  force_destroy = true
}

resource "aws_s3_bucket_website_configuration" "this" {
  bucket = "example-bucket-abc123"
}

What this control checks

This control checks that no aws_s3_bucket_website_configuration resource exists for a given S3 bucket. In the current AWS provider, static website hosting uses the standalone aws_s3_bucket_website_configuration resource with nested blocks: index_document, error_document, redirect_all_requests_to, and routing_rule. A bucket passes when no such resource references it. It fails if any aws_s3_bucket_website_configuration resource exists, regardless of which documents or routing rules are configured. For buckets still using the older inline website block on aws_s3_bucket, the same applies: any website configuration present means the bucket fails.

Common pitfalls

  • Deprecated inline website block still enables hosting

    The legacy website block inside aws_s3_bucket still enables static website hosting, and upgrading to provider v4+ does not remove it automatically. If your Terraform was written against the old provider and you have not explicitly removed that block, the bucket has website hosting enabled regardless of what else you have configured. Remove the inline website block and confirm no aws_s3_bucket_website_configuration resource replaces it.

  • Removing website config breaks existing references

    The S3 website endpoint (<bucket>.s3-website-<region>.amazonaws.com) is different from the standard S3 REST endpoint. If Route 53 alias records, CloudFront origins, or application code reference the website endpoint, removing the aws_s3_bucket_website_configuration resource causes those references to return errors. Migrate consumers to the REST endpoint or a CloudFront distribution before removing the configuration.

  • Terraform import can reintroduce website config

    Running terraform import aws_s3_bucket_website_configuration.<name> <bucket> on a bucket that already has website hosting enabled pulls that configuration into state. If someone imports it to 'manage' the existing setup without realizing this control forbids it, the bucket stays non-compliant and the import itself is the problem.

  • redirect_all_requests_to still counts as website hosting

    Using aws_s3_bucket_website_configuration solely for redirect_all_requests_to (domain redirect use cases) does not exempt a bucket from this control. The S3 website endpoint is active regardless of whether any objects are being served. The bucket fails.

Audit evidence

An auditor expects evidence that no S3 buckets in the account have static website hosting enabled. AWS Config rule results for s3-bucket-website-disabled provide continuous evaluation. Alternatively, aws s3api get-bucket-website --bucket <name> returning NoSuchWebsiteConfiguration for each bucket demonstrates compliance. Screenshots from the S3 console showing 'Static website hosting' set to 'Disabled' under the Properties tab are acceptable point-in-time evidence.

For accounts with many buckets, a consolidated report from AWS Security Hub or a CSPM tool listing each bucket's website hosting status is more practical and auditable than individual screenshots.

Tool mappings

Use these identifiers to cross-reference this control across tools, reports, and evidence.

  • Compliance.tf Control: s3_bucket_static_website_hosting_disabled

  • Powerpipe Control: aws_compliance.control.s3_bucket_static_website_hosting_disabled

  • KICS Query: 42bb6b7f-6d54-4428-b707-666f669d94fb

Last reviewed: 2026-03-09