From ff6018e0763bc5692b342e6693621655ef44d929 Mon Sep 17 00:00:00 2001 From: Vidminas <5411598+Vidminas@users.noreply.github.com> Date: Fri, 9 Oct 2026 11:22:22 +0100 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=94=90=20docs:=20Document=20Serving?= =?UTF-8?q?=20S3=20Files=20Through=20LibreChat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documents STORAGE_PROXY_FILES (LibreChat-AI/LibreChat#16914): LibreChat serves S3 files itself, through /api/stored-files links that never expire, so the bucket can stay private. The S3 page gains a section on who can open a link, an example bucket policy that admits only a VPC endpoint, and the trade-offs; the environment variable table and the storage overview point to it. Co-Authored-By: Claude Opus 5.5 --- content/docs/configuration/cdn/index.mdx | 2 ++ content/docs/configuration/cdn/s3.mdx | 34 +++++++++++++++++++++++- content/docs/configuration/dotenv.mdx | 6 +++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/content/docs/configuration/cdn/index.mdx b/content/docs/configuration/cdn/index.mdx index 99d38d8cc..93f310ce2 100644 --- a/content/docs/configuration/cdn/index.mdx +++ b/content/docs/configuration/cdn/index.mdx @@ -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 files + itself](/docs/configuration/cdn/s3#serve-files-through-librechat). ## File Storage diff --git a/content/docs/configuration/cdn/s3.mdx b/content/docs/configuration/cdn/s3.mdx index bdd02ec54..ce653af75 100644 --- a/content/docs/configuration/cdn/s3.mdx +++ b/content/docs/configuration/cdn/s3.mdx @@ -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. @@ -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. @@ -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: @@ -135,6 +137,36 @@ fileStrategies: +## 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/`) 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):** diff --git a/content/docs/configuration/dotenv.mdx b/content/docs/configuration/dotenv.mdx index 063de7456..a9f04392b 100644 --- a/content/docs/configuration/dotenv.mdx +++ b/content/docs/configuration/dotenv.mdx @@ -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. See Serve Files Through LibreChat on the S3 page. Default: false.', + '# STORAGE_PROXY_FILES=false', + ], [ 'CLOUDFRONT_KEY_PAIR_ID', 'string', From decd738c55de7d589a25c5f3a38826d530d06e9d Mon Sep 17 00:00:00 2001 From: Vidminas <5411598+Vidminas@users.noreply.github.com> Date: Fri, 9 Oct 2026 11:22:54 +0100 Subject: [PATCH 2/2] =?UTF-8?q?=E2=98=81=EF=B8=8F=20docs:=20Document=20Ser?= =?UTF-8?q?ving=20Azure=20Blob=20Files=20Through=20LibreChat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documents STORAGE_PROXY_FILES for Azure Blob Storage (LibreChat-AI/LibreChat#16915): LibreChat serves blobs itself, through /api/stored-files links, so images and downloads work from a private container without SAS URLs. The Azure page gains a section on who can open a link and the trade-offs, including that only the configured container is served; the environment variable table and the storage overview point to it. Co-Authored-By: Claude Opus 5.5 --- content/docs/configuration/cdn/azure.mdx | 22 +++++++++++++++++++++- content/docs/configuration/cdn/index.mdx | 4 ++-- content/docs/configuration/dotenv.mdx | 8 +++++++- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/content/docs/configuration/cdn/azure.mdx b/content/docs/configuration/cdn/azure.mdx index 92ffbbc06..4ca667141 100644 --- a/content/docs/configuration/cdn/azure.mdx +++ b/content/docs/configuration/cdn/azure.mdx @@ -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 @@ -102,6 +104,24 @@ fileStrategy: "azure_blob" ``` +## 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/`), 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 diff --git a/content/docs/configuration/cdn/index.mdx b/content/docs/configuration/cdn/index.mdx index 93f310ce2..1a918ac7d 100644 --- a/content/docs/configuration/cdn/index.mdx +++ b/content/docs/configuration/cdn/index.mdx @@ -10,8 +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 files - itself](/docs/configuration/cdn/s3#serve-files-through-librechat). + 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. ## File Storage diff --git a/content/docs/configuration/dotenv.mdx b/content/docs/configuration/dotenv.mdx index a9f04392b..48eb38338 100644 --- a/content/docs/configuration/dotenv.mdx +++ b/content/docs/configuration/dotenv.mdx @@ -3679,7 +3679,7 @@ See: **[Amazon S3 Configuration](/docs/configuration/cdn/s3)** and **[CloudFront [ '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. See Serve Files Through LibreChat on the S3 page. Default: false.', + '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', ], [ @@ -3734,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', + ], ]} />