CDK Private S3 Hosting Construct
This is a CDK construct that creates a private S3 bucket and an Application Load Balancer (ALB) with a listener rule that forwards requests to the S3 bucket.
You can use this construct for a enterprise use case where you want to host a static website in a private network.
Original idea is from this blog post. And some implementations are referenced from this post.
Architecture
Installation
You can install the package via npm:
npm install cdk-private-s3-hosting
Usage
To create a private S3 bucket and an ALB with a listener rule that forwards requests to the S3 bucket, you can use the following code:
import { PrivateS3Hosting } from 'cdk-private-s3-hosting';
const privateS3Hosting = new PrivateS3Hosting(this, 'PrivateS3Hosting', {
domainName: 'cryer-nao-domain.com',
});
After you deploy the stack, you can access the S3 bucket using the ALB's DNS name from the VPC where the stack is deployed.
For example, if you put the hoge.txt file in the root of S3 bucket, you can access it using the following command:
curl http://cryer-nao-domain.com/hoge.txt
Use existing VPC
You can use an existing VPC by specifying the vpc property.
declare const vpc: ec2.IVpc;
const privateS3Hosting = new PrivateS3Hosting(this, 'PrivateS3Hosting', {
domainName: 'cryer-nao-domain.com',
vpc,
});
Specify the sub domain
You can specify the sub domain by setting the subDomain property.
const privateS3Hosting = new PrivateS3Hosting(this, 'PrivateS3Hosting', {
domainName: 'cryer-nao-domain.com',
subDomain: 'sub',
});
In this case, the S3 bucket name will be created with ${subDomain}.${domainName}.
If enablePrivateDns is enabled, a private hosted zone will also be created for the domainName and an A record will be created from ${subDomain}.${domainName} to the ALB DNS name.
You can retrieve hoge.txt on the root of the S3 bucket using the following command:
curl http://sub.cryer-nao-domain.com/hoge.txt
Deploy the frontend assets
You can deploy the frontend assets to the S3 bucket like below:
import { PrivateS3Hosting } from 'cdk-private-s3-hosting';
import * as s3deploy from 'aws-cdk-lib/aws-s3-deployment';
const privateS3Hosting = new PrivateS3Hosting(this, 'PrivateS3Hosting', {
domainName: 'cryer-nao-domain.com',
});
new s3deploy.BucketDeployment(this, 'DeployWebsite', {
sources: [s3deploy.Source.asset('./website-dist')],
destinationBucket: privateS3Hosting.bucket,
});
After deploying the stack, you can access the website using the domainName you specified from the VPC.
[cloudshell-user@ip-10-0-31-170 ~]$ curl http://cryer-nao-domain.com/ -L
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<script type="module" crossorigin src="/assets/index-f40OySzR.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DiwrgTda.css">
</head>
<body>
<div id="root"></div>
</body>
</html>
Note: All access to the path pattern */ will be redirected to /index.html. Therefore, it will function correctly even when the path is set on the frontend and the page is reloaded.
Note: I also recommend to use deploy-time-build to build the frontend assets while deploying the stack.
Setup DNS
This construct creates Route53 hosted zone and an A record for the domain name you specified by default.
If you want to use your own DNS settings(e.g. using a corporate DNS server),
you can disable the Route53 hosted zone creation by setting the enablePrivateDns property to false.
import { PrivateS3Hosting } from 'cdk-private-s3-hosting';
const privateS3Hosting = new PrivateS3Hosting(this, 'PrivateS3Hosting', {
domainName: 'cryer-nao-domain.com',
enablePrivateDns: false,
});
TLS Certificate
If you want to use HTTPS, you need to create a TLS certificate in ACM and pass it to the certificate property.
import * as acm from 'aws-cdk-lib/aws-certificatemanager';
import { PrivateS3Hosting } from 'cdk-private-s3-hosting';
declare const certificate: acm.ICertificate;
const privateS3Hosting = new PrivateS3Hosting(this, 'PrivateS3Hosting', {
domainName: 'cryer-nao-domain.com',
certificate,
});
Of course, specified domain name (domainName and subDomain) must be the same as the domain name of the certificate.
Metadata
Release files for cdk-private-s3-hosting 0.0.18
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| cdk_private_s3_hosting-0.0.18.tar.gz | 63.9 kB | With provenance | Details for cdk_private_s3_hosting-0.0.18.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| cdk_private_s3_hosting-0.0.18-py3-none-any.whl | Python 3 | none | any | Details for cdk_private_s3_hosting-0.0.18-py3-none-any.whl |
Total release size: 125.9 kB
Release files / cdk_private_s3_hosting-0.0.18.tar.gz
| Download URL | cdk_private_s3_hosting-0.0.18.tar.gz |
|---|---|
| Size | 63.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d1319aee11b22f481d689df18003a23efdbe450a5b312dff2a22cd0a8f58589f
|
|
BLAKE2b-256 checksum How to use checksums |
545c46bf5fad748d0d6d0f32fe777d9742e43fb149b67f55c669f9f42f87e8e6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.14.6
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 20, 2026.
Transparency logRelease files / cdk_private_s3_hosting-0.0.18-py3-none-any.whl
| Download URL | cdk_private_s3_hosting-0.0.18-py3-none-any.whl |
|---|---|
| Size | 62.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
733e7fc83f10f7bdbad75166004f7862aa65388447ffc3c79194b7d246be805d
|
|
BLAKE2b-256 checksum How to use checksums |
4aad0f590bcfdc27112016a1eb0cbfd6d236526931391c981657d7930572c242
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.14.6
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 20, 2026.
Transparency logSecurity for cdk-private-s3-hosting 0.0.18
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for cdk_private_s3_hosting-0.0.18.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 20, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.