Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Upload file content type charset is dropped

Open
#816 7 comments 0 reactions 1 assignee View on GitHub

Maintainers usually reply within 1 day

@ferhatelmas is already working on this.

Since Feb 3, 2026.

  • #975 by @SushanthMusham — closed without merging

Assessment

This issue has not been assessed yet.

Description

accepted bug

Bug report

  • I confirm this is a bug with Supabase, not with my own application.
  • I confirm I have searched the Docs, GitHub Discussions, and Discord.

Describe the bug

When uploading text files to Supabase Storage using the Python SDK with a
Content-Type that includes a charset parameter (for example
text/markdown; charset=UTF-8), the charset information is silently dropped
during the upload process.

Although the upload request explicitly includes the full Content-Type
(including charset), after the upload completes:

  • The file metadata returned by the Storage info / list API
  • The HTTP response headers returned by public URLs or signed URLs

only contain the base MIME type (e.g. text/markdown) and never include the
charset parameter.

This leads to incorrect or ambiguous character encoding handling in browsers and
HTTP clients that rely on response headers for charset detection.


To Reproduce

1. Upload a file with charset specified
from supabase import create_client

supabase = create_client(SUPABASE_URL, SUPABASE_KEY)

with open("example.md", "rb") as f:
    supabase.storage.from_("test-bucket").upload(
        "example.md",
        f,
        file_options={
            "content-type": "text/markdown; charset=UTF-8"
        }
    )
2. Verify upload request
  • The actual HTTP request sent by the Python SDK is multipart/form-data
  • The file part includes:
Content-Type: text/markdown; charset=UTF-8

3. Query file metadata
files = supabase.storage.from_("test-bucket").list()

Returned metadata shows:

contentType: "text/markdown"

❌ charset=UTF-8 is missing.


4. Access via signed URL or public URL
url = supabase.storage.from_("test-bucket").create_signed_url(
    "example.md", 60
)["signedURL"]
curl -I "<signed-url>"

Response header:

Content-Type: text/markdown

❌ charset=UTF-8 is missing.


Expected behavior

If a file is uploaded with:

Content-Type: text/markdown; charset=UTF-8

Supabase Storage should either:

  1. Preserve the full Content-Type (including charset) in stored metadata and
    HTTP responses
    or
  2. Clearly document that MIME parameters (such as charset) are intentionally
    stripped and unsupported

Root cause analysis (from backend code inspection)

After inspecting the Supabase Storage backend source code, the root cause is in
src/storage/backend/s3/adapter.ts, specifically the uploadObject method:

The Problem
  1. Content-Type is correctly extracted: During multipart uploads, the backend
    correctly extracts the full Content-Type (including charset) from
    formData.fields.contentType?.value in fileUploadFromRequest
    (src/storage/uploader.ts:344).

  2. Content-Type is correctly sent to S3: The full Content-Type (including
    charset) is passed to S3's Upload command as the ContentType parameter
    (src/storage/backend/s3/adapter.ts:152).

  3. Charset is lost when retrieving metadata: After upload completes, for
    non-zero-byte files, the code calls headObject to retrieve metadata
    (src/storage/backend/s3/adapter.ts:175). S3's HeadObjectCommand returns a
    ContentType that has been normalized/stripped of the charset parameter.

  4. Normalized value overwrites original: The code uses the mimetype from
    headObject response (src/storage/backend/s3/adapter.ts:190) instead of the
    original contentType parameter, causing the charset to be lost.

Code Evidence
// src/storage/backend/s3/adapter.ts:174-190
const metadata = hasUploadedBytes
  ? await this.headObject(bucketName, key, version)
  : {
      mimetype: contentType,
      // ...
    }

return {
  // ...
  mimetype: metadata.mimetype,
  // ...
}
Why Zero-Byte Files Work

For zero-byte files, the code bypasses headObject and directly uses the
original contentType parameter (line 179), which is why charset is
preserved for empty files but not for files with content.

The Fix

The code should use the original contentType parameter for non-zero-byte
files instead of relying on headObject's normalized response, or merge the
original contentType with other metadata from headObject.


Why this is a bug (not a feature request)

  • charset is a standard, semantically significant part of Content-Type
  • Browsers and HTTP clients rely on it for correct text decoding
  • The Python SDK explicitly sends the full Content-Type
  • Supabase Storage silently drops it without documentation or warning

System information

  • OS: macOS / Linux
  • SDK: supabase==2.27.0, storage3==2.27.0
  • Python version: 3.12
  • Storage: Supabase managed Storage
Dominant language
TypeScript
Stars
1.3k
Forks
314
Avg merge
1d 22h
Merged PRs (30d)
77

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from supabase/storage

All issues in supabase/storage

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.