Skip to content

S3_PUBLIC_ENDPOINT forces path-style presigned URLs, breaking Cloudflare R2 custom domains #2872

Description

@rzkyyds

Summary

When self-hosting InstantDB with Cloudflare R2 as the S3 backend, presigned URLs for Storage ($files) are always generated in path-style (https:////). Cloudflare R2 custom domains do not support presigned URLs at all (Cloudflare docs: "Presigned URLs work with the S3 API domain and cannot be used with custom domains"), and R2 custom domains serve the bucket at the root (/), so any URL that includes the bucket name in the path returns 404 NoSuchKey.

Current State

  • config/s3-public-endpoint + presign path (server/src/instant/storage/s3.clj:156-158) force :path-style? true whenever S3_PUBLIC_ENDPOINT is set.
  • server/src/instant/util/aws_signature.clj (presign-s3-url, lines 288-337) builds url-path as "/" + bucket + path when path-style? is true.
  • Result for R2 custom domain (files.example.com): https://files.example.com////?X-Amz-... → R2 looks up object //... → 404 (bucket is implicit at root on R2 custom domains).
  • For R2 public dev URL (*.r2.dev), the same path-style URL also 404s, and additionally the host serves a certificate mismatch (SEC_E_WRONG_PRINCIPAL/ERR_CERT_COMMON_NAME_INVALID) on the current pub-.r2.dev hostname.

Workaround

  • MinIO (self-hosted) works because MinIO supports path-style + presigned URLs (the official VPS guide). Set S3_ENDPOINT=http://minio:9000, S3_PUBLIC_ENDPOINT=https://files.example.com.
  • Backblaze B2 also works (path-style + presigned supported), no custom-domain requirement for the API.
  • R2 users can only use the S3 API domain (https://<ACCOUNT_ID>.r2.cloudflarestorage.com) as S3_ENDPOINT, but cannot get clean custom-domain URLs without a Cloudflare Worker (which must also handle presigning, since R2 custom domains reject SigV4).

Where They Break

  • Any deployment that sets S3_PUBLIC_ENDPOINT to a host that does not accept path-style URLs (R2 custom domain, R2 .r2.dev dev URL, and any S3-compatible storage that only supports virtual-hosted style).
  • There is no flag to opt out of path-style; it's hardcoded when S3_PUBLIC_ENDPOINT is present (s3.clj:156-158).
  • Also affects PUT presigned uploads (browser uploads to $files), not just GET.

The Ask

  1. Add S3_VIRTUAL_HOST_STYLE opt-out env (path-style → virtual-hosted) di presign-creds + S3 client config.
  2. Document R2 caveat: custom domain + presigned URLs not supported (Cloudflare); supported path = S3 API domain.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions