Using S3 presigned upload URLs with Hetzners object storage

Learn how to configure CORS policies for Hetzner's S3-compatible object storage, enabling presigned URLs for direct user uploads in your web apps

October 19, 2024

As you may have seen, Hezner recently announced a S3-compatible object storage offering. S3-compatible storage, also offered by the likes of Cloudflare and Backblaze, is great because you can use the same AWS SDKs you're probably familiar without being locked into any specific provider.

When you create a bucket in Hetzner, you get an access key and secret key which will work out of the box with things like the AWS JS SDK, but at the time of writing, there's no provided way to configure a CORS policy for the bucket. Without a CORS policy your bucket will block uploads from presigned URLs in which case you'll have to handle user-uploaded files on your API server rather than letting the user upload to the bucket directly.

There is a way to add the CORS policy to the bucket, however, which I'll outline here. Before starting, ensure you have a bucket already set up and have stored the access key ID and secret key somewhere safely.

AWS CLI

S3-compatible storage providers decide which S3 APIs they're support and thankfully Hetzner support plenty of them. Because of this, we can use the native AWS CLI to perform us to perform traditional AWS S3 API commands to our Hetzner-based bucket.

Follow the guide in the Hetzner docs to get your CLI set up with your bucket credentials. At the end of it you should have files that look like this.

# ~/.aws/configure [hetzner] endpoint_url = https://fsn1.your-objectstorage.com
# ~/.aws/credentials [hetzner] aws_access_key_id=key-id-here aws_secret_access_key=secret-access-key-here

Note: While the endpoint_url is defined in here, I couldn't get the CLI to pick it up and always have to pass the --endpoint-url to commands directly, but your results may vary

To test that this is working, run the following command and ensure that it works.

aws s3 ls \ --endpoint-url=https://fsn1.your-objectstorage.com \ --profile hetzner

CORS policy

S3 allows you to define your CORS policy in a standard JSON format. For the sake if this demo, let's say we want to allow GET, PUT, and POST commands to our bucket from our web app's origin (https://example.com). Here's what the CORS policy for this looks like as a JSON object.

{ "CORSRules": [ { "AllowedOrigins": ["https://example.com"], "AllowedMethods": ["GET", "PUT", "POST"], "AllowedHeaders": [ "Authorization", "Content-Type", "x-amz-date", "x-amz-content-sha256", "x-amz-user-agent" ], "ExposeHeaders": [ "x-amz-server-side-encryption", "x-amz-request-id", "x-amz-id-2", "ETag" ], "MaxAgeSeconds": 600 } ] }

Save this file with, your own modifications, to a local file called cors.json.

Applying the policy

Now that we have the policy and the credentials set up, we can simply run the put-bucket-cors command with our Hetzner object storage credentials, and optionally the endpoint if your CLI profile doesn't pick it up, and you're done. If successful, the command will produce no output.

aws s3api put-bucket-cors \ --bucket your-bucket-name \ --endpoint-url=https://fsn1.your-objectstorage.com \ --cors-configuration file://cors.json \ --profile hetzner

If you found this useful, please consider sharing!


If you're looking for a SaaS starter kit with S3-compatible storage, check out Launchway. As well as S3-compatible storage provider for file uploads, Launchway also offers authentication, subscriptions, database connections, tons of UI components and more to get a SaaS started.

me
Brian BriscoeCreator of Launchway