Skip to content

[Enhancement]: S3 Implementation incompatible with Hetzner (Missing forcePathStyle configuration) #11806

@Dual-0

Description

@Dual-0

What features would you like to see added?

Description:
I am attempting to configure LibreChat to use Hetzner Object Storage (S3-compatible) as the CDN file strategy.

Hetzner (like MinIO and older Ceph setups) requires Path-Style Access (e.g., https://endpoint/bucket/key) because their SSL certificates do not support the Virtual-Hosted-Style subdomains (e.g., https://bucket.endpoint/key) that the AWS SDK defaults to.

Currently, packages/api/src/cdn/s3.ts hardcodes the S3Client configuration and ignores any forcePathStyle setting. This makes it impossible to use LibreChat with these providers without patching the code at runtime.

Steps to Reproduce:

  1. Configure .env with S3-compatible credentials that require path-style access (e.g., Hetzner fsn1):
    AWS_REGION="fsn1"
    AWS_ENDPOINT_URL="https://fsn1.your-objectstorage.com"
    AWS_BUCKET_NAME="my-uuid-bucket"
    AWS_ACCESS_KEY_ID="xxx"
    AWS_SECRET_ACCESS_KEY="xxx"
    # This variable is currently ignored by LibreChat:
    AWS_FORCE_PATH_STYLE="true"
  2. Configure librechat.yaml: fileStrategy: "s3"
  3. Attempt to upload a file (e.g., an avatar or image).

Actual Behavior:
The upload fails with a 401 Unauthorized or SignatureDoesNotMatch error.
The browser receives a presigned URL in Virtual-Hosted style:
https://my-uuid-bucket.fsn1.your-objectstorage.com/images/...

This URL fails because the SSL certificate for *.your-objectstorage.com does not cover the nested bucket subdomain, or the provider rejects the signature structure.

Expected Behavior:
LibreChat should respect an environment variable (e.g., AWS_FORCE_PATH_STYLE) to enable Path-Style access, generating URLs like:
https://fsn1.your-objectstorage.com/my-uuid-bucket/images/...

More details

Root Cause Analysis:
In packages/api/src/cdn/s3.ts, the initializeS3 function initializes the client with region and endpoint but omits forcePathStyle.

Proof of Fix (Verified in Container):
I ran the following Node.js script inside the LibreChat container using the exact same AWS SDK present in node_modules.

  • Test A (Default/Current Behavior): Fails with 401/Signature Error.
  • Test B (With forcePathStyle): Succeeds immediately.
const { S3Client, PutObjectCommand } = require("@aws-sdk/client-s3");

// Successful Configuration
const config = {
  region: process.env.AWS_REGION,
  endpoint: process.env.AWS_ENDPOINT_URL,
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
  },
  // This is the missing key in LibreChat:
  forcePathStyle: true
};

const client = new S3Client(config);

// This works perfectly when forcePathStyle is true
const cmd = new PutObjectCommand({
  Bucket: process.env.AWS_BUCKET_NAME,
  Key: "test_upload.txt",
  Body: "Test"
});
client.send(cmd).then(() => console.log("Success"));

Proposed Solution:
Update packages/api/src/cdn/s3.ts to include forcePathStyle in the config object:

const config = {
  region,
  // Add this line:
  forcePathStyle: process.env.AWS_FORCE_PATH_STYLE === 'true',
  ...(endpoint ? { endpoint } : {}),
};

Which components are impacted by your request?

General

Pictures

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions