403 SignatureDoesNotMatch when hitting Supabase CLI Storage via tunnel (/storage/v1/s3 with rclone/awscli)
Describe the bug
When accessing the Supabase CLI’s S3-compatible Storage endpoint through a public tunnel, every SigV4 request returns 403 SignatureDoesNotMatch. The client signs Host=<public-host> and path=/storage/v1/s3, but responses consistently include Via: kong, suggesting the local proxy rewrites host/path so the storage signer computes a different canonical request.
To Reproduce
- Start Supabase CLI (local dev stack) so Storage is available at http://localhost:54123/storage/v1/s3 and create S3 Access/Secret keys from supabase status.
- Expose the port publicly using any of:
- ngrok (with host header preservation), Cloudflare Tunnel, or Tailscale Funnel.
- The public URL looks like https://
/storage/v1/s3
- Configure rclone:
[supabase]
type = s3
provider = Minio
endpoint = https://<public-host>/storage/v1/s3
region = us-east-1 # also tried: local
force_path_style = true
v2_auth = false
access_key_id = ****
secret_access_key = ****
- Create an empty bucket, e.g., memory-filesystem, then run:
- rclone -vv --dump headers --dump bodies lsd supabase:memory-filesystem
- aws --debug --endpoint-url https://
/storage/v1/s3 --region us-east-1 s3 ls
- Both return 403 SignatureDoesNotMatch.
Expected behavior
SigV4 verification should succeed when the client signs Host=
System information
-
Version of OS: macOS 14 (darwin 24.6.0)
-
Version of CLI: supabase 2.45.5
-
Version of Docker: Docker version 28.4.0, build d8eb465
-
Version of Docker Compose: Docker Compose version v2.39.2-desktop.1
-
Other client tools: rclone v1.60.1 (Debian build), awscli 1.42.40 (botocore 1.40.40)
-
Versions of services (supabase services):
SERVICE IMAGE LOCAL supabase/postgres 15.8.1.085 supabase/gotrue v2.179.0 postgrest/postgrest v13.0.7 supabase/realtime v2.51.3 supabase/storage-api v1.27.4 supabase/edge-runtime v1.69.12 supabase/studio 2025.09.22-sha-7b3007d supabase/postgres-meta v0.91.6 supabase/logflare 1.22.3 supabase/supavisor 2.7.0
Additional context
- Tunnels tried: ngrok (with --host-header preserve), Cloudflare Quick Tunnel, Tailscale Funnel. All produce the same failure.
- Regions tried:
localandus-east-1. Provider tried:Minio.force_path_style=true. - Keys: using “S3 Access Key/Secret Key” from supabase status (not service role/JWT). Keys are not included here.
AWS CLI debug
CanonicalRequest:
GET
/storage/v1/s3
host:<public-host>
x-amz-content-sha256:e3b0c442...
x-amz-date:20250928T191635Z
host;x-amz-content-sha256;x-amz-date
e3b0c442...
HTTP/2.0 403 Forbidden
Content-Type: application/xml; charset=utf-8
Via: kong/2.8.1
<Error><Code>SignatureDoesNotMatch</Code>...</Error>
Observation / hypothesis
Because every response contains Via: kong and fails with SignatureDoesNotMatch, it appears the local proxy may be rewriting the request (e.g., stripping or reshaping the /storage/v1/ prefix or altering Host) before the storage service verifies SigV4, causing a canonical URI mismatch.
Questions
- What’s the recommended way for external clients to reach the Supabase CLI Storage S3 endpoint with SigV4 so the signer sees the same Host and canonical URI? Is /storage/v1/s3 intended to be signed as-is?
- Is there a documented way to bypass Kong locally (non‑proxied storage endpoint) so we can target the storage S3 gateway directly?
- If this is a known limitation of the local stack, what configuration or workflow do you recommend for local development (e.g., using a hosted project endpoint or alternative local endpoint)?
Hi!, did you find some workaround for this?