Private bucket origins

Updated 23 Sep 2026

A private bucket origin lets a zone pull from a bucket that is closed to the public. The edge signs every request it makes to the bucket with a read-only key pair you give the zone, and nothing about the bucket, its keys or its error messages reaches your viewers.

Which buckets#

Amazon S3, and anything that speaks the same API: Bunny Storage, Backblaze B2, Cloudflare R2, DigitalOcean Spaces, MinIO. Caching, slicing, byte ranges, the origin shield, purging and statistics all work exactly as they do on a standard origin; only the request that leaves the edge is different.

The bucket details#

With Private S3 bucket (signed requests) as the origin type, the zone takes:

  • Bucket Endpoint: the store's address, such as https://s3.eu-west-1.amazonaws.com. A scheme, a hostname and an optional port, with no path.
  • Bucket Name: 3 to 63 characters of lower-case letters, digits, dots and hyphens.
  • Region: the one the bucket lives in, such as eu-west-1 or de. Type auto for Cloudflare R2 and other stores without regions of their own; most such stores also accept us-east-1. It goes into every signature, so a wrong one is refused by the store.
  • Bucket Addressing: Path style fetches endpoint/bucket/file; Virtual hosted fetches bucket.endpoint/file and needs the endpoint to start with the bucket name. The zone page shows the URL a file would be fetched from as you type. Path style is the one to pick unless your provider's endpoint already begins with the bucket name.
  • Access Key and Secret Key: a pair that can read this bucket and nothing else. The secret is stored once and never shown again; leave the field blank on a later save to keep it, or type a new one to replace it.

Two mistakes are caught at save time rather than as a stream of failed requests: virtual hosted addressing with an endpoint that does not start with the bucket name, and, on a shared store such as Amazon S3, path style with an endpoint that already does, which would ask for the bucket twice. A store on your own domain whose bucket happens to share its first label saves as it is.

Keys that can only read#

Give the zone a key pair that can do nothing but read this one bucket. On Amazon S3 that is a policy like this one, attached to a user created for the purpose:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject"],
      "Resource": "arn:aws:s3:::my-video-bucket/*"
    },
    {
      "Effect": "Allow",
      "Action": ["s3:ListBucket"],
      "Resource": "arn:aws:s3:::my-video-bucket"
    }
  ]
}

The second statement is not there so the edge can list anything; it never does. Amazon answers a request for a file that does not exist with 403 rather than 404 when the key is not allowed to list the bucket, and a 403 from the bucket becomes our error page rather than a plain not-found. Other stores have their own way of making a read-only key; the same rule applies.

What the edge does#

  • Every request to the bucket is a GET or a HEAD, signed with AWS Signature Version 4. Anything else a viewer sends is answered 405 with Allow: GET, HEAD (a POST is refused with 403 instead while Block POST Requests is on), so nothing can write to the bucket through the zone, and a browser's CORS preflight is refused too.
  • The viewer's query string is never sent to the bucket. Signed with your key it would reach the store's listing and versioning operations. It still tells one file from another in the cache, so ?v=2 works as a cache buster.
  • A redirect from the bucket is never followed, and the Host header is always the bucket's own, so the Forward Host Header, Follow Redirects and Add Canonical Headers switches are unused and shown greyed.
  • The signature covers the request itself and not the conditional headers, so byte ranges and cache revalidation work as on any other origin.

What your viewers never see#

Every x-amz-* header is removed from the response; they name the bucket, its region and the store's request ids. A 401 or 403 from the bucket, which carries your access key id and the whole request in its body, becomes our error page for an origin that is not responding, with a 502 status, and is never cached, so the next request tries again the moment the keys are fixed. Every other answer keeps its status: a missing file is still a 404, but with a one-line body instead of the store's XML.

Cache lifetimes on a bucket#

A store sends no Cache-Control unless you set one on the object, so with the zone on Respect Origin Cache-Control the defaults in How caching works apply: seven days for video, audio, downloads and any file of 10 MB or more, a day for images, five minutes for JSON under an API-shaped path, 10 seconds for a playlist, and an hour for the rest. Set a Cache Expiration Time on the zone if you want one figure for everything.

Switching the origin type back to a standard origin clears the bucket name, region, addressing and both keys in the same save. From the API the zone reports S3_SECRET_KEY_SET as 1 or 0 and never the secret itself; see Zones.

Ask a human

To
Subject
Docs: Private bucket origins

Read and answered by the people who build CacheGenie, seven days a week.

Write to us