Repository navigation
Static Website Hosting
VersityGW can expose bucket objects as static websites through a dedicated HTTP or HTTPS listener. Website configuration is managed through the normal, authenticated S3 API, while browsers access content anonymously through the website endpoint.
The website endpoint supports:
-
GET,HEAD, and CORS preflightOPTIONSrequests. - Index and custom error documents.
- Whole-site, routing-rule, and per-object redirects.
- Virtual-host routing for multiple website buckets.
- Object HTTP headers, user metadata, and bucket CORS rules.
It is not an alternate S3 API endpoint. Uploads, bucket administration, and
signed S3 requests continue to use the main --port listener.
The following example runs the S3 API on port 7070 and website hosting on port 8080:
versitygw \
--port :7070 \
--website :8080 \
--website-domain example.com \
[other global options] \
posix /srv/versitygw| Option | Environment variable | Purpose |
|---|---|---|
--website <address> |
VGW_WEBSITE_PORT |
Enables the website listener. It accepts the same TCP and UNIX-socket address forms as --port and may be specified more than once. |
--website-domain <domain> |
VGW_WEBSITE_DOMAIN |
Selects the base domain used to map request hosts to bucket names. |
--website-cert <file> |
VGW_WEBSITE_CERT |
TLS certificate for the website listener. |
--website-key <file> |
VGW_WEBSITE_KEY |
TLS private key for the website listener. |
--website-no-tls |
VGW_WEBSITE_NO_TLS |
Forces plaintext HTTP even when the main gateway has TLS configured. |
If website-specific certificate files are omitted, the listener inherits the
main --cert and --key files. The certificate and key must always be
configured as a pair. SIGHUP reloads website certificates along with the
other gateway certificates.
The website and S3 API listeners must use different addresses. Website traffic normally uses ports 80 or 443 through a reverse proxy or load balancer, while the S3 API remains on its own endpoint.
With --website-domain example.com, VersityGW maps hosts as follows:
| Request host | Bucket |
|---|---|
blog.example.com |
blog |
docs.example.com |
docs |
example.com |
example.com |
Only one label may precede the configured domain. For example,
www.blog.example.com does not resolve to a bucket. Configure wildcard DNS
such as *.example.com for subdomain buckets and an apex record for
example.com if the apex bucket will be used.
For HTTPS, the certificate must cover the names clients use. A wildcard
certificate for *.example.com does not normally cover the apex
example.com, so include both names when serving both forms.
When --website-domain is omitted, the listener runs in catch-all mode
and treats the complete request hostname as the bucket name:
| Request host | Bucket |
|---|---|
blog.example.com |
blog.example.com |
mysite.example.net |
mysite.example.net |
Catch-all mode is useful for buckets named after complete custom domains.
Although the CLI help describes --website-domain as required, an omitted
domain is accepted and logged as initializing website endpoint (catch-all).
The examples below assume:
- S3 API endpoint:
https://s3.example.com - Website endpoint:
http://blog.example.com:8080 - Bucket:
blog
Create the bucket and upload the site through the authenticated S3 endpoint:
aws --endpoint-url https://s3.example.com s3 mb s3://blog
aws --endpoint-url https://s3.example.com s3 cp ./site/ s3://blog/ \
--recursiveSet appropriate content types while uploading. The website endpoint returns
the stored Content-Type, Cache-Control, Content-Encoding,
Content-Language, Content-Disposition, Expires, ETag, and
Last-Modified headers; it does not infer or rewrite them when serving the
object.
Website requests do not carry S3 credentials. Objects must therefore allow
anonymous s3:GetObject access through a bucket policy or ACL. A minimal
bucket policy is:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicWebsiteRead",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::blog/*"
}
]
}Save it as public-read.json, then apply it through the S3 endpoint:
aws --endpoint-url https://s3.example.com s3api put-bucket-policy \
--bucket blog \
--policy file://public-read.jsonConfiguring a website does not make a bucket public. If public object access
is missing, the website endpoint returns 403 AccessDenied. The configured
error document is also fetched as an anonymous object and cannot be served
unless it is public.
Create website.json:
{
"IndexDocument": {
"Suffix": "index.html"
},
"ErrorDocument": {
"Key": "error.html"
}
}Apply it:
aws --endpoint-url https://s3.example.com s3api put-bucket-website \
--bucket blog \
--website-configuration file://website.jsonThe index suffix is appended only to the site root and paths ending in /:
| Website request | Object read |
|---|---|
/ |
index.html |
/docs/ |
docs/index.html |
/docs |
docs |
The suffix must be non-empty and cannot contain /. If an object read fails
and ErrorDocument is configured, the gateway serves that object's body and
headers while retaining the original error status, such as 404. If the
error document is absent or private, its own error response is returned.
Verify the website without changing local DNS:
curl --resolve blog.example.com:8080:127.0.0.1 \
http://blog.example.com:8080/VersityGW implements the S3 website redirect forms in this order:
RedirectAllRequestsTo- A matching prefix-only routing rule
- An object's
x-amz-website-redirect-location - A routing rule matching an object-read error
- The configured error document
Redirect targets preserve the original query string. Keys are URL-encoded in
the generated Location header.
RedirectAllRequestsTo is mutually exclusive with index, error, and routing
rule configuration:
{
"RedirectAllRequestsTo": {
"HostName": "www.example.com",
"Protocol": "https"
}
}Every request receives a 301 redirect. Object public access is not required
because no object is read.
The following configuration moves a prefix before reading an object and
redirects remaining 404 responses:
{
"IndexDocument": {
"Suffix": "index.html"
},
"ErrorDocument": {
"Key": "error.html"
},
"RoutingRules": [
{
"Condition": {
"KeyPrefixEquals": "old-docs/"
},
"Redirect": {
"ReplaceKeyPrefixWith": "docs/",
"HttpRedirectCode": "301"
}
},
{
"Condition": {
"HttpErrorCodeReturnedEquals": "404"
},
"Redirect": {
"HostName": "fallback.example.com",
"ReplaceKeyWith": "not-found",
"Protocol": "https",
"HttpRedirectCode": "302"
}
}
]
}Rules are evaluated in configuration order. A condition may contain
KeyPrefixEquals, HttpErrorCodeReturnedEquals, or both. A rule with only a
key prefix is applied before object access is checked; a rule with an error
code is evaluated after the object read fails. Configuration is limited to
50 routing rules and 128 KiB.
ReplaceKeyWith and ReplaceKeyPrefixWith cannot appear in the same
redirect. An omitted redirect code defaults to 301, and the supported
explicit codes are 301, 302, 303, 304, 305, 307, and 308.
Redirect protocols are limited to http and https.
Set x-amz-website-redirect-location while uploading an object:
aws --endpoint-url https://s3.example.com s3api put-object \
--bucket blog \
--key old-page.html \
--website-redirect-location /new-page.html \
--body old-page.htmlA successful website lookup of old-page.html returns a 301 with the
stored location instead of the object body. The object must still be
publicly readable before its redirect metadata can be evaluated.
Website GET and HEAD responses use the bucket's S3 CORS configuration.
The website endpoint also handles browser preflight OPTIONS requests.
Configure CORS with PutBucketCors when site content must be fetched by
scripts running on another origin. Without a matching bucket CORS rule, a
preflight request is rejected.
POST, PUT, PATCH, and DELETE are not supported on the website
listener and return 405 MethodNotAllowed with:
Allow: GET, HEAD, OPTIONSUse the authenticated S3 endpoint:
aws --endpoint-url https://s3.example.com s3api get-bucket-website \
--bucket blog
aws --endpoint-url https://s3.example.com s3api delete-bucket-website \
--bucket blogDeleting the configuration leaves the bucket and objects unchanged. Website
requests then return NoSuchWebsiteConfiguration.
Website configuration and object serving are supported by the built-in
POSIX, ScoutFS, Azure Blob, and S3 proxy backends. A plugin backend must
implement PutBucketWebsite, GetBucketWebsite, and
DeleteBucketWebsite, in addition to public object reads, to support this
endpoint.
Home · Quickstart · Configuration · Operations · Testing · Developer Guide · GitHub · Discussions · Issues
Apache 2.0 · @versitysoftware · LinkedIn · X · Facebook · Instagram
- Home
- Key Features
- User Guide
- Getting Started
- Networking and Deployment
- Access Control / IAM
- Features
- Backends
- Compatibility
- Operations
- Metrics
- Admin APIs
- Logging
- S3 RDMA
- Developer Guide
- Articles