S3 API overview
STRG speaks the Amazon S3 REST API, so the tools you may already know - the AWS CLI, the AWS SDKs and other S3 clients - work with your buckets as they are. All they need is a STRG endpoint and a STRG access key to sign with. This is the API for an application storing its uploads, a nightly backup job, or an agent keeping its files in a bucket.
This page gathers what every operation has in common: where to send requests, how to sign them, and the handful of places where STRG behaves differently from Amazon S3. Each operation has a page of its own, listed under Supported operations.
Regions and endpoints
Every bucket lives in one region, picked when the bucket is created. Each region has its own endpoint, and an endpoint answers for the buckets in its own region.
| Region | Name | Endpoint (path-style) | Virtual-hosted form |
|---|---|---|---|
us-east-1 | US East | https://s3.us-east-1.strg.com | https://<bucket>.s3.us-east-1.strg.com |
eu-central-1 | EU Central | https://s3.eu-central-1.strg.com | https://<bucket>.s3.eu-central-1.strg.com |
A client is configured with both the endpoint and its matching region, because the region is part
of the signature. A request signed for a region other than the endpoint's answers
400 AuthorizationHeaderMalformed (400 AuthorizationQueryParametersError for a presigned URL),
and the error message names the region the endpoint expects.
Addressing
S3 has two ways of naming a bucket in a URL, and STRG understands both.
| Style | Request for photos/2024/cat.jpg in my-bucket |
|---|---|
| Path-style | https://s3.eu-central-1.strg.com/my-bucket/photos/2024/cat.jpg |
| Virtual-hosted | https://my-bucket.s3.eu-central-1.strg.com/photos/2024/cat.jpg |
A bucket name containing a dot (my.bucket) works with path-style addressing. In
virtual-hosted form the TLS handshake fails before the request is even sent. For such a bucket,
the client setting is forcePathStyle: true (JavaScript), addressing_style: "path" (boto3) or
s3.addressing_style = path (AWS CLI).
Client configuration
The AWS CLI and the SDKs read your credentials from the environment: AWS_ACCESS_KEY_ID holds
your access key's ID and AWS_SECRET_ACCESS_KEY its secret. Each example below lists the objects
in my-bucket in eu-central-1, once per addressing style.
Path-style
- AWS CLI
- Python (boto3)
- JavaScript (AWS SDK v3)
aws configure set default.s3.addressing_style path
aws s3api list-objects-v2 \
--bucket my-bucket \
--endpoint-url https://s3.eu-central-1.strg.com \
--region eu-central-1
import boto3
from botocore.config import Config
s3 = boto3.client(
"s3",
endpoint_url="https://s3.eu-central-1.strg.com",
region_name="eu-central-1",
config=Config(s3={"addressing_style": "path"}),
)
for obj in s3.list_objects_v2(Bucket="my-bucket").get("Contents", []):
print(obj["Key"], obj["Size"])
import { S3Client, ListObjectsV2Command } from "@aws-sdk/client-s3";
const s3 = new S3Client({
endpoint: "https://s3.eu-central-1.strg.com",
region: "eu-central-1",
forcePathStyle: true,
});
const { Contents = [] } = await s3.send(new ListObjectsV2Command({ Bucket: "my-bucket" }));
for (const obj of Contents) console.log(obj.Key, obj.Size);
Virtual-hosted
- AWS CLI
- Python (boto3)
- JavaScript (AWS SDK v3)
aws configure set default.s3.addressing_style virtual
aws s3api list-objects-v2 \
--bucket my-bucket \
--endpoint-url https://s3.eu-central-1.strg.com \
--region eu-central-1
import boto3
from botocore.config import Config
s3 = boto3.client(
"s3",
endpoint_url="https://s3.eu-central-1.strg.com",
region_name="eu-central-1",
config=Config(s3={"addressing_style": "virtual"}),
)
for obj in s3.list_objects_v2(Bucket="my-bucket").get("Contents", []):
print(obj["Key"], obj["Size"])
import { S3Client, ListObjectsV2Command } from "@aws-sdk/client-s3";
const s3 = new S3Client({
endpoint: "https://s3.eu-central-1.strg.com",
region: "eu-central-1",
forcePathStyle: false,
});
const { Contents = [] } = await s3.send(new ListObjectsV2Command({ Bucket: "my-bucket" }));
for (const obj of Contents) console.log(obj.Key, obj.Size);
The SDKs' defaults - request checksums and aws-chunked streaming uploads included - work as they
are, with nothing to switch off.
Authentication
Every signed request uses AWS Signature Version 4 (AWS4-HMAC-SHA256), with an access key created
on the Access Keys page of the dashboard, through the MCP server or through the
REST API. Access keys describes the permissions a
key can carry.
The table shows each way of signing and how STRG handles it.
| Header signing | The Authorization header |
| Presigned URLs | Query-string signing, X-Amz-Expires from 1 second to 7 days (604,800 seconds) |
| Streaming uploads | aws-chunked bodies (STREAMING-AWS4-HMAC-SHA256-PAYLOAD, STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER, STREAMING-UNSIGNED-PAYLOAD-TRAILER) on PutObject and UploadPart, with every chunk verified. Trailing checksums: CRC32, CRC32C, CRC64NVME, SHA-1 and SHA-256. A presigned URL carries its body in one piece instead: aws-chunked in a presigned URL answers 400 InvalidRequest |
UNSIGNED-PAYLOAD | Accepted |
| Clock skew | Up to 15 minutes between X-Amz-Date and the server's clock; beyond that, 403 RequestTimeTooSkewed |
x-amz-* headers | Every one is part of the signature. A request carrying an unsigned x-amz-* header answers 403 AccessDenied |
| Unsigned (anonymous) requests | Welcome for GetObject and HeadObject on a bucket whose Bucket Protection is off - Anonymous reads has the details. Every other unsigned request, listing included, answers 403 AccessDenied |
STRG keeps to Signature Version 4 and long-lived access keys, so a few other mechanisms get a clear answer instead:
| Mechanism | Answer |
|---|---|
Signature Version 2 (AWS AKID:signature) | 400 InvalidRequest |
SigV4A (AWS4-ECDSA-P256-SHA256) | 400 InvalidRequest |
Session tokens (x-amz-security-token, X-Amz-Security-Token) and S3 Express sessions (x-amz-s3session-token) | 400 InvalidToken - STRG has no temporary credentials, so there is no token to send |
Browser POST uploads with a policy document | 501 NotImplemented |
A revoked access key, or a change to the account behind one, takes effect within 60 seconds.
Anonymous reads
A bucket whose Bucket Protection is off is public: anyone can read its objects without signing, which suits a website's images or files meant to be shared by link. The read goes to the bucket's own regional endpoint, path-style or virtual-hosted:
curl https://my-bucket.s3.eu-central-1.strg.com/photos/2024/cat.jpg
Here is how each kind of anonymous request is answered:
| Request | Answer |
|---|---|
GET or HEAD of an object in a public bucket | The object, as for a signed request. Range, versionId and conditional headers work |
| Listing a public bucket, or any other operation | 403 AccessDenied |
A response-* query parameter (response-content-type, ...) | 400 InvalidRequest, as on Amazon S3 |
| A bucket with Bucket Protection on | 403 AccessDenied |
| A bucket that does not exist, or is in another region | 404 NoSuchBucket |
A bucket created a moment ago can answer 404 NoSuchBucket to anonymous requests for a few
seconds, until every endpoint has heard about it.
Anonymous reads can be answered from a cache, which makes popular public files quick to fetch. That
happens for an object stored with a Cache-Control that lets a shared cache keep it
(public, max-age=60, for example); an object without one is never cached. A cached answer is
kept for its max-age (or s-maxage) or 5 minutes, whichever is shorter, and the reader is
still sent the object's own Cache-Control. The cache is never cleared early, so for up to
5 minutes after an object is overwritten or deleted, Bucket Protection is turned on, or the
bucket's CORS rules change, anonymous readers can still get the previous
answer. Signed requests never use the cache.
CORS
A browser asks permission with a CORS preflight (OPTIONS) before a web page talks to your bucket
directly. STRG answers it on the bucket's regional endpoint from the bucket's
CORS rules, as Amazon S3 does, and a preflight the rules do not allow answers
403 AccessForbidden.
403 AccountProblem
CreateBucket answers 403 AccountProblem while your account has no verified card on file: a card
comes before the first bucket, and Billing in the dashboard, https://app.strg.com/billing, is
where it is added. A card is required explains why. Once the
card is there, the same request succeeds.
Differences from Amazon S3
STRG behaves like Amazon S3 almost everywhere. Where it differs, it is usually because there is less to configure - and the table lists each place, so nothing comes as a surprise.
| Area | Amazon S3 | STRG |
|---|---|---|
| Bucket in another region | 301 with x-amz-bucket-region, which the SDKs follow | 404 NoSuchBucket. Each endpoint answers for its own region, and the SDKs do not retry, so a client set to another region hears that a bucket does not exist even while the dashboard shows it. Setting the client to the bucket's region is all it takes |
| Nothing stored in the endpoint's region yet | Any valid key reaches the endpoint | The plain truth: 404 NoSuchBucket for a bucket operation, an empty ListBuckets result, and 501 NotImplemented for an operation STRG does not offer. CreateBucket creates the bucket |
| A key created moments ago | Works at once | In a region where you already store data, a brand-new key can take a moment to arrive: until it does, every operation but CreateBucket answers 403 InvalidAccessKeyId, and a retry shortly afterwards succeeds |
CreateBucket | Creates in any region | Creates the bucket in the endpoint's region - any region, your first bucket there included. A LocationConstraint naming another region answers 400 InvalidLocationConstraint. A first bucket also sets up storage in the region, which takes a little longer - up to about 90 seconds - and from then on the key that signed the request works in that region too. A creation still running at that point answers 503 SlowDown with Retry-After; the SDKs retry automatically, and the retry joins the same creation rather than starting another |
| Object Lock at creation | x-amz-bucket-object-lock-enabled: true on CreateBucket | Two steps: the bucket is created, then PutObjectLockConfiguration enables Object Lock. x-amz-bucket-object-lock-enabled: true on CreateBucket answers 501 NotImplemented |
| Deleting and re-creating a bucket | A policy naming the bucket keeps applying | A bucket re-created under a deleted bucket's name is a new bucket, so an access key limited to the old one does not reach it. A new key for the new bucket takes only a moment to create |
| Bucket names | 3-63 characters | 3-58 characters, with the same rules otherwise. Names are globally unique across STRG |
GetBucketLocation | The region | The STRG region label, and an empty LocationConstraint for us-east-1, as S3 answers it |
ListBuckets | Every bucket | The buckets in the endpoint's region, all in one answer - it is never paginated - and without BucketRegion. A key limited to specific buckets lists them when it holds the List all bucket names permission; without it, ListBuckets answers 403 AccessDenied |
HeadBucket | 403 for a bucket you cannot access | 404 for a bucket that is not yours, exactly as for one that does not exist |
| Multipart parts | 5 MiB to 5 GiB, 1-10,000 parts | 5,000,000 bytes (except the last part) to 5 GiB, part numbers 1-10,000 |
| Incomplete multipart uploads | Billed | Billed as stored data until completed or aborted. A lifecycle policy can abort them for you |
| Versioning | Off until enabled; can be suspended | Always on, so an earlier version is always there to go back to. A delete writes a delete marker, and every version is billed as stored data until it is deleted or a lifecycle policy removes it. PutBucketVersioning with Suspended answers 501 NotImplemented |
| Storage classes | Several | STANDARD, for every object - nothing to choose between |
| Encryption | SSE-S3, SSE-KMS, SSE-C | Server-side encryption with STRG-managed keys (AES256) as a bucket default, and SSE-C per request. SSE-KMS answers 501 NotImplemented. GetBucketEncryption answers AES256 even for a bucket never given a default |
| ACLs | Grants and canned ACLs | One canned ACL per bucket, private or public-read. An object shares its bucket's ACL, so PutObjectAcl succeeds when it names the bucket's own and answers 501 NotImplemented otherwise. Grant headers and XML grant bodies answer 501 NotImplemented |
| Tagging | Object and bucket tags | No tags: object and bucket tag sets are always empty, so tools that read them still work, and x-amz-tagging on an upload or copy answers 501 NotImplemented |
| Conditional writes | If-None-Match / If-Match on PutObject, CompleteMultipartUpload and CopyObject | 501 NotImplemented, so a client knows straight away |
| Bucket policies, public access block, ownership controls, notifications, acceleration | Configurable | Nothing to configure. The read operations answer as AWS does for a bucket never configured, so tools that probe them work as usual |
x-amz-expected-bucket-owner, x-amz-source-expected-bucket-owner | Checked | Welcome, with nothing to check: a key only ever reaches buckets of its own account |
Object keys with . or .. path segments | Stored and read as sent | A key with a . or .. segment (a/../b, a/./b), percent-encoded as %2e or not, cannot travel safely in a URL: the request answers 400 InvalidURI, and nothing is stored or read under any name. DeleteObjects, which carries the key in the request body, removes an existing one |
| Keys limited to a prefix | - | A listing passes the key's prefix as prefix (or a longer one); any other listing answers 403 AccessDenied |
| Streaming a body without a length | Allowed in some cases | Transfer-Encoding: chunked without Content-Length answers 411 MissingContentLength. aws-chunked uploads work as usual |
| Throttling | 503 SlowDown | 503 SlowDown with Retry-After, which the SDKs retry automatically |
Each operation's page has the differences particular to it.
Supported operations
Buckets
| Operation | |
|---|---|
| ListBuckets | Lists the buckets in the endpoint's region |
| HeadBucket | Tells whether a bucket exists and you can reach it |
| GetBucketLocation | Answers a bucket's region |
| CreateBucket | Creates a bucket in the endpoint's region |
| DeleteBucket | Deletes an empty bucket |
Bucket configuration
| Operation | |
|---|---|
| GetBucketAcl, PutBucketAcl | private or public-read |
| GetBucketCors, PutBucketCors, DeleteBucketCors | CORS rules |
| GetBucketLifecycleConfiguration, PutBucketLifecycleConfiguration, DeleteBucketLifecycle | Lifecycle policies |
| GetBucketLifecycle, PutBucketLifecycle | The older, deprecated forms of the above, still answered |
| GetBucketEncryption, PutBucketEncryption | Default encryption, AES256 |
| GetBucketVersioning, PutBucketVersioning | Always Enabled |
| GetBucketTagging, DeleteBucketTagging | Empty tag sets, for tools that read them |
| GetBucketAccelerateConfiguration, GetBucketNotification, GetBucketNotificationConfiguration, GetBucketOwnershipControls, GetBucketPolicy, GetBucketRequestPayment, GetPublicAccessBlock | The never-configured answer, for tools that probe them |
Objects
| Operation | |
|---|---|
| PutObject | Uploads an object of up to 5 GiB |
| GetObject, HeadObject | Reads an object, or its metadata |
| CopyObject | Copies an object |
| DeleteObject, DeleteObjects | Write delete markers, or delete versions permanently |
| ListObjectsV2, ListObjects, ListObjectVersions | List objects, or every version and delete marker |
| GetObjectAcl, PutObjectAcl | The bucket's ACL |
| GetObjectTagging, DeleteObjectTagging | Empty tag sets, for tools that read them |
Multipart uploads
Multipart uploads carry a large object in parts, each retried on its own if a connection drops.
| Operation | |
|---|---|
| CreateMultipartUpload | Starts an upload |
| UploadPart, UploadPartCopy | Uploads a part, or copies one from an existing object |
| CompleteMultipartUpload, AbortMultipartUpload | Finish or discard an upload |
| ListParts, ListMultipartUploads | Show the uploads in progress and their parts |
Object Lock
| Operation | |
|---|---|
| GetObjectLockConfiguration, PutObjectLockConfiguration | A bucket's Object Lock setting and default retention |
| GetObjectRetention, PutObjectRetention | An object version's retention |
| GetObjectLegalHold, PutObjectLegalHold | An object version's legal hold |
Enabling Object Lock on a bucket is permanent: it can never be switched off again. Locked object
versions stay billed as stored data until their lock expires. Object Lock
explains both before a PutObjectLockConfiguration call makes them true for a bucket.
Other S3 operations
The rest of the S3 API answers 501 NotImplemented, so a client knows straight away - as does a
request with a query parameter none of the operations above accepts:
CreateBucketMetadataConfiguration, CreateBucketMetadataTableConfiguration,
CreateSession, DeleteBucketAnalyticsConfiguration, DeleteBucketEncryption,
DeleteBucketIntelligentTieringConfiguration, DeleteBucketInventoryConfiguration,
DeleteBucketMetadataConfiguration, DeleteBucketMetadataTableConfiguration,
DeleteBucketMetricsConfiguration, DeleteBucketOwnershipControls, DeleteBucketPolicy,
DeleteBucketReplication, DeleteBucketWebsite, DeleteObjectAnnotation,
DeletePublicAccessBlock, GetBucketAbac, GetBucketAnalyticsConfiguration,
GetBucketIntelligentTieringConfiguration, GetBucketInventoryConfiguration, GetBucketLogging,
GetBucketMetadataConfiguration, GetBucketMetadataTableConfiguration,
GetBucketMetricsConfiguration, GetBucketPolicyStatus, GetBucketReplication,
GetBucketWebsite, GetObjectAnnotation, GetObjectAttributes, GetObjectTorrent,
ListBucketAnalyticsConfigurations, ListBucketIntelligentTieringConfigurations,
ListBucketInventoryConfigurations, ListBucketMetricsConfigurations, ListDirectoryBuckets,
ListObjectAnnotations, PutBucketAbac, PutBucketAccelerateConfiguration,
PutBucketAnalyticsConfiguration, PutBucketIntelligentTieringConfiguration,
PutBucketInventoryConfiguration, PutBucketLogging, PutBucketMetricsConfiguration,
PutBucketNotification, PutBucketNotificationConfiguration, PutBucketOwnershipControls,
PutBucketPolicy, PutBucketReplication, PutBucketRequestPayment, PutBucketTagging,
PutBucketWebsite, PutObjectAnnotation, PutObjectTagging, PutPublicAccessBlock,
RenameObject, RestoreObject, SelectObjectContent,
UpdateBucketMetadataAnnotationTableConfiguration,
UpdateBucketMetadataInventoryTableConfiguration,
UpdateBucketMetadataJournalTableConfiguration, UpdateObjectEncryption,
WriteGetObjectResponse.