Strapi plugin logo for Hetzner Object Storage

Hetzner Object Storage

A Strapi Upload Provider for Hetzner Object Storage (S3-compatible).

strapi-provider-upload-hetzner-s3

A Strapi Upload Provider for Hetzner Object Storage (S3-compatible).

🚀 Features

  • ✅ Full integration with Hetzner Object Storage
  • ✅ Support for all three Hetzner regions (FSN1, NBG1, HEL1)
  • ✅ TypeScript Support
  • ✅ Bucket prefixes for organized file structure
  • ✅ Custom Base URL Support (e.g., for CDN)
  • ✅ ACL configuration
  • ✅ Unit Tests
  • ✅ Buffer and Stream Upload Support

📋 Prerequisites

  • Node.js >= 14.19.1
  • Strapi >= 4.0.0
  • A Hetzner Object Storage Bucket

📦 Installation

npm install strapi-provider-upload-hetzner-s3
# or
yarn add strapi-provider-upload-hetzner-s3

🔧 Hetzner Object Storage Setup

1. Create Bucket

  1. Go to Hetzner Cloud Console
  2. Navigate to "Object Storage"
  3. Create a new bucket
  4. Choose a region:
    • FSN1: Falkenstein, Germany
    • NBG1: Nuremberg, Germany
    • HEL1: Helsinki, Finland

2. Generate Access Keys

  1. Click on your bucket
  2. Go to "S3 Keys"
  3. Create a new S3 Key
  4. Save the Access Key and Secret Key securely

3. Bucket Settings

  • Public Access: Enable this if your files should be publicly accessible
  • CORS: Configure CORS settings if needed

⚙️ Configuration

Basic Configuration

Create or edit ./config/plugins.js (or .ts):

1module.exports = ({ env }) => ({
2  upload: {
3    config: {
4      provider: "strapi-provider-upload-hetzner-s3",
5      providerOptions: {
6        accessKeyId: env("HETZNER_ACCESS_KEY_ID"),
7        secretAccessKey: env("HETZNER_SECRET_ACCESS_KEY"),
8        region: env("HETZNER_REGION"), // fsn1, nbg1, or hel1
9        params: {
10          Bucket: env("HETZNER_BUCKET_NAME"),
11        },
12      },
13    },
14  },
15});

Advanced Configuration

1module.exports = ({ env }) => ({
2  upload: {
3    config: {
4      provider: "strapi-provider-upload-hetzner-s3",
5      providerOptions: {
6        accessKeyId: env("HETZNER_ACCESS_KEY_ID"),
7        secretAccessKey: env("HETZNER_SECRET_ACCESS_KEY"),
8        region: env("HETZNER_REGION"), // fsn1, nbg1, or hel1
9        params: {
10          Bucket: env("HETZNER_BUCKET_NAME"),
11          ACL: "public-read", // Optional: makes files publicly readable
12        },
13        // Optional: Prefix for all uploads
14        prefix: env("HETZNER_BUCKET_PREFIX", "uploads"), // e.g., "strapi-assets"
15        // Optional: Custom Base URL (e.g., for CDN)
16        baseUrl: env("CDN_BASE_URL"), // e.g., "https://cdn.example.com"
17      },
18    },
19  },
20});

Environment Variables

Create a .env file in the root of your project:

1# Hetzner Object Storage Credentials
2HETZNER_ACCESS_KEY_ID=your_access_key
3HETZNER_SECRET_ACCESS_KEY=your_secret_key
4
5# Region (fsn1, nbg1, or hel1)
6HETZNER_REGION=fsn1
7
8# Bucket Name
9HETZNER_BUCKET_NAME=my-strapi-bucket
10
11# Optional: Bucket Prefix
12HETZNER_BUCKET_PREFIX=uploads
13
14# Optional: CDN Base URL
15CDN_BASE_URL=https://cdn.example.com

🌍 Available Regions

Region CodeLocationEndpoint
fsn1Falkenstein, Germanyfsn1.your-objectstorage.com
nbg1Nuremberg, Germanynbg1.your-objectstorage.com
hel1Helsinki, Finlandhel1.your-objectstorage.com

🖼️ Image Previews in Strapi Admin

To display thumbnails correctly in the Strapi Admin Panel, configure the Content Security Policy:

Edit ./config/middlewares.js:

1module.exports = ({ env }) => [
2  // ... other middlewares
3  {
4    name: "strapi::security",
5    config: {
6      contentSecurityPolicy: {
7        useDefaults: true,
8        directives: {
9          "connect-src": ["'self'", "https:"],
10          "img-src": [
11            "'self'",
12            "data:",
13            "blob:",
14            `${env("HETZNER_BUCKET_NAME")}.${env("HETZNER_REGION")}.your-objectstorage.com`,
15          ],
16          "media-src": [
17            "'self'",
18            "data:",
19            "blob:",
20            `${env("HETZNER_BUCKET_NAME")}.${env("HETZNER_REGION")}.your-objectstorage.com`,
21          ],
22          upgradeInsecureRequests: null,
23        },
24      },
25    },
26  },
27  // ... other middlewares
28];

If using a CDN Base URL:

1"img-src": [
2  "'self'",
3  "data:",
4  "blob:",
5  env("CDN_BASE_URL"),
6],
7"media-src": [
8  "'self'",
9  "data:",
10  "blob:",
11  env("CDN_BASE_URL"),
12],

🔐 Security & Best Practices

ACL Settings

Hetzner Object Storage supports the following ACL values:

  • private (Default): Only the bucket owner has access
  • public-read: Everyone can read objects
  • public-read-write: Everyone can read and write (⚠️ not recommended)
  • authenticated-read: Only authenticated users can read

Recommendation: Use public-read for public websites or omit ACL and configure bucket settings via Hetzner Console.

Bucket Organization with Prefixes

Use prefixes to organize your files:

1prefix: "production/uploads"  // All files under production/uploads/
2prefix: "strapi/media"         // All files under strapi/media/

CORS Configuration

If your frontend directly accesses the files, configure CORS in the Hetzner Console:

1{
2  "CORSRules": [
3    {
4      "AllowedOrigins": ["https://your-website.com"],
5      "AllowedMethods": ["GET", "HEAD"],
6      "AllowedHeaders": ["*"],
7      "MaxAgeSeconds": 3000
8    }
9  ]
10}

🚨 Troubleshooting

Error: "Access Denied"

Solution:

  • Check your Access Key and Secret Key
  • Ensure the key has the correct permissions
  • If using ACL, ensure the key has s3:PutObjectACL permission

Error: "Bucket not found"

Solution:

  • Check the bucket name (case-sensitive!)
  • Ensure the bucket exists in the correct region
  • Verify the region configuration (fsn1, nbg1, or hel1)

Images not displaying

Solution:

  • Check the Content Security Policy in middlewares.js
  • Ensure the bucket is publicly readable or ACL is set correctly
  • Check browser console for CORS errors

Uploads failing

Solution:

  • Check bucket size and limits
  • Ensure the upload isn't too large
  • Check network connection
  • Enable debug logging in Strapi

🧪 Testing

# Run tests
npm test

# Tests with coverage
npm test -- --coverage

# Build
npm run build

📊 Comparison to Other Providers

FeatureHetzner S3AWS S3DigitalOcean Spaces
Price (Storage)~€0.005/GB~€0.023/GB~€0.020/GB
Price (Transfer)Free~€0.09/GB~€0.01/GB
EU Regions✅ 3✅ 8+
S3-Compatible
GDPR-Compliant⚠️⚠️

🤝 Contributing

Contributions are welcome! Please create a pull request or open an issue.

Development Setup

# Clone repository
git clone https://github.com/raiva-technologies/strapi-provider-upload-hetzner-s3.git

# Install dependencies
npm install

# Run tests
npm test

# Build
npm run build

📝 License

MIT License - see LICENSE file

🔗 Links

💬 Support

If you have any questions or issues:

📈 Changelog

v1.0.0

  • Initial Release
  • Support for all three Hetzner regions
  • TypeScript Support
  • Full test coverage
  • Prefix and Base URL Support

Made with ❤️ for the Strapi Community

Install now

npm install strapi-provider-upload-hetzner-s3

STATS

No GitHub star yetNot downloaded this week

Last updated

52 days ago

Strapi Version

Unavailable

Author

github profile image for RAIVA Technologies UG
RAIVA Technologies UG

Related plugin

Media Upload

Useful links

Create your own plugin

Check out the available plugin resources that will help you to develop your plugin or provider and get it listed on the marketplace.