Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion content/docs/configuration/cdn/azure.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,14 @@ AZURE_STORAGE_ACCOUNT_NAME=yourAccountName

AZURE_STORAGE_PUBLIC_ACCESS=false
AZURE_CONTAINER_NAME=files
# STORAGE_PROXY_FILES=false
```

- **AZURE_STORAGE_CONNECTION_STRING:** Set this if you are using Option A.
- **AZURE_STORAGE_ACCOUNT_NAME:** Set this if you are using Option B (Managed Identity). Do not set both.
- **AZURE_STORAGE_PUBLIC_ACCESS:** Set to `false` if you do not want your blobs to be publicly accessible by default. Set to `true` if you need public access (for example, for publicly viewable images).
- **AZURE_STORAGE_PUBLIC_ACCESS:** Set to `false` if you do not want your blobs to be publicly accessible by default. Set to `true` if you need public access (for example, for publicly viewable images), unless LibreChat [serves the files itself](#serve-files-through-librechat).
- **AZURE_CONTAINER_NAME:** This is the container name your application will use (e.g., `files`). The application will automatically create this container if it doesn’t exist.
- **STORAGE_PROXY_FILES:** (Optional) Set to `true` to serve files through LibreChat, so images and downloads work from a private container. See [Serve Files Through LibreChat](#serve-files-through-librechat). Default: `false`.

## 4. Configure LibreChat to Use Azure Blob Storage

Expand All @@ -102,6 +104,24 @@ fileStrategy: "azure_blob"
```
</Callout>

## Serve Files Through LibreChat

By default LibreChat stores each blob's URL, so browsers can only display images from a container with public access. Set `STORAGE_PROXY_FILES=true` to have LibreChat serve blobs itself instead: stored links point at LibreChat (`/api/stored-files/azure_blob/<blob path>`), LibreChat reads each blob with its own credentials, and the container can stay private. The links never expire, so there is nothing to refresh.

```bash filename=".env"
AZURE_STORAGE_PUBLIC_ACCESS=false
STORAGE_PROXY_FILES=true
```

LibreChat serves a file to the signed-in user who owns it, avatars to any signed-in user, and anyone else the file's own access rules allow, such as users of an agent the file is attached to. Access never crosses tenants, and everyone else gets a 404. Only raster images are shown inline; other files are downloaded.

Keep in mind:

- **File bytes pass through the LibreChat server,** as they do with local storage.
- **Only the configured container is served this way.** Blobs in another container keep their URLs.
- **API clients** receive relative links that need a signed-in session.
- **Existing links:** links stored before you turn the setting on keep their blob URLs.

---

## Summary
Expand Down
2 changes: 2 additions & 0 deletions content/docs/configuration/cdn/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { S3Icon, AzureIcon, AWSIcon, FirebaseIcon } from '@/components/icons/pro
LibreChat supports local storage, object storage backends, and CDN-backed delivery. Use an object
storage backend such as S3 or Azure Blob Storage for durable file storage, then add a CDN like
CloudFront when you need stable media links, edge caching, signed cookies, or signed download URLs.
For stable links without a CDN, LibreChat can also serve [S3](/docs/configuration/cdn/s3#serve-files-through-librechat)
and [Azure Blob Storage](/docs/configuration/cdn/azure#serve-files-through-librechat) files itself.
</Callout>

## File Storage
Expand Down
34 changes: 33 additions & 1 deletion content/docs/configuration/cdn/s3.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ AWS_REGION=your_selected_region
AWS_BUCKET_NAME=your_bucket_name
AWS_ENDPOINT_URL=https://your_endpoint_url
# AWS_FORCE_PATH_STYLE=false
# STORAGE_PROXY_FILES=false
```

- **AWS_ACCESS_KEY_ID:** Your IAM user's access key.
Expand All @@ -106,6 +107,7 @@ AWS_ENDPOINT_URL=https://your_endpoint_url
- **AWS_FORCE_PATH_STYLE:** (Optional) Set to `true` for providers that require path-style URLs (`endpoint/bucket/key`) rather than virtual-hosted-style (`bucket.endpoint/key`). Required for Hetzner Object Storage, MinIO, and similar providers whose SSL certificates don't cover bucket subdomains. Not needed for AWS S3 or Cloudflare R2. Default: `false`.
- **S3_URL_EXPIRY_SECONDS:** (Optional) Lifetime of each presigned URL, in seconds. See the note on presigned URLs below for the provider-side caps that apply.
- **S3_REFRESH_EXPIRY_MS:** (Optional) Regenerate a presigned URL once it reaches this age, in milliseconds, instead of using the default expiry-buffer logic. Unset by default. A value that is not a positive integer is ignored with a warning in the logs.
- **STORAGE_PROXY_FILES:** (Optional) Set to `true` to serve files through LibreChat instead of presigned URLs. See [Serve Files Through LibreChat](#serve-files-through-librechat). Default: `false`.

If you are using **IRSA** on Kubernetes, you do **not** need to set `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` in your environment. The AWS SDK will automatically obtain temporary credentials via the service account assigned to your pod. Ensure that `AWS_REGION` and `AWS_BUCKET_NAME` are still provided.

Expand All @@ -124,7 +126,7 @@ fileStrategy: 's3'

The refresh logic in LibreChat is not applied consistently for every visual surface. For example, list-style endpoints can return stored URLs while detail endpoints refresh them. This can cause visible broken avatar images in the model selector and chat UI. See the [related discussion](https://github.com/LibreChat-AI/LibreChat/discussions/10280#discussioncomment-14803903) for full context.

**S3 is well-suited for document storage** (PDFs, text files, code) where short-lived presigned download URLs are appropriate. For images and avatars that need to render persistently across the UI, use [CloudFront with S3](/docs/configuration/cdn/cloudfront), [Firebase](/docs/configuration/cdn/firebase), or configure `fileStrategies` to route only those types to a CDN-backed strategy:
**S3 is well-suited for document storage** (PDFs, text files, code) where short-lived presigned download URLs are appropriate. For images and avatars that need to render persistently across the UI, [serve files through LibreChat](#serve-files-through-librechat), use [CloudFront with S3](/docs/configuration/cdn/cloudfront) or [Firebase](/docs/configuration/cdn/firebase), or configure `fileStrategies` to route only those types to a CDN-backed strategy:

```yaml filename="librechat.yaml"
fileStrategies:
Expand All @@ -135,6 +137,36 @@ fileStrategies:

</Callout>

## Serve Files Through LibreChat

Set `STORAGE_PROXY_FILES=true` to have LibreChat serve S3 files itself instead of giving browsers presigned URLs. Stored links point at LibreChat (`/api/stored-files/s3/<key>`) and never expire, and browsers never contact S3, so the bucket can stay private to your network. This works with AWS S3 and S3-compatible services, and needs no CDN or custom domain.

```bash filename=".env"
STORAGE_PROXY_FILES=true
```

LibreChat serves a file to the signed-in user who owns it, avatars to any signed-in user, and anyone else the file's own access rules allow, such as users of an agent the file is attached to. Access never crosses tenants, and everyone else gets a 404. Only raster images are shown inline; other files are downloaded.

With browsers out of the picture, you can deny object access to anything but your own network. For example, this bucket policy statement refuses reads and writes that don't arrive through your VPC's S3 gateway endpoint:

```json filename="Bucket policy statement"
{
"Sid": "DenyObjectAccessOutsideVpcEndpoint",
"Effect": "Deny",
"Principal": "*",
"Action": ["s3:GetObject*", "s3:PutObject*", "s3:DeleteObject*"],
"Resource": "arn:aws:s3:::your_bucket_name/*",
"Condition": { "StringNotEquals": { "aws:SourceVpce": "vpce-0123456789abcdef0" } }
}
```

Keep in mind:

- **File bytes pass through the LibreChat server,** as they do with local storage.
- **Downloads stream through LibreChat** instead of redirecting to S3.
- **API clients** receive relative links that need a signed-in session.
- **Existing links:** links stored before you turn the setting on keep their presigned URLs. The file list and avatars replace theirs as they load.

## Summary

1. **Create an AWS Account & IAM User (or configure IRSA):**
Expand Down
12 changes: 12 additions & 0 deletions content/docs/configuration/dotenv.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3676,6 +3676,12 @@ See: **[Amazon S3 Configuration](/docs/configuration/cdn/s3)** and **[CloudFront
'Set to true for S3-compatible providers that require path-style URLs (e.g. MinIO, Hetzner, Backblaze B2). Not needed for AWS S3. Default: false.',
'# AWS_FORCE_PATH_STYLE=false',
],
[
'STORAGE_PROXY_FILES',
'boolean',
'Serve stored files through LibreChat (/api/stored-files) instead of presigned URLs, so links never expire and the bucket can stay private. Also applies to Azure Blob Storage. See Serve Files Through LibreChat on the S3 page. Default: false.',
'# STORAGE_PROXY_FILES=false',
],
[
'CLOUDFRONT_KEY_PAIR_ID',
'string',
Expand Down Expand Up @@ -3728,6 +3734,12 @@ See: **[Azure Blob Storage CDN Configuration](/docs/configuration/cdn/azure)**
'Container name for file storage. Default: files.',
'AZURE_CONTAINER_NAME=files',
],
[
'STORAGE_PROXY_FILES',
'boolean',
'Serve blobs through LibreChat (/api/stored-files) instead of blob URLs, so images work from a private container. See Serve Files Through LibreChat on the Azure page. Default: false.',
'# STORAGE_PROXY_FILES=false',
],
]}
/>

Expand Down