@uppy/aws-s3 v6: signRequest cannot supply headers required by a presigned URL
Initial checklist
- I understand this is a bug report and questions should be posted in the Community Forum.
- I searched existing issues and could not find a report covering this case.
Steps to reproduce
Version: @uppy/aws-s3 6.1.0, using signRequest for a direct, single-part PUT upload.
A signing endpoint can generate a presigned PutObject URL with both Content-Type and Content-Disposition included in the signature. For example, using the AWS SDK for Ruby:
url = Aws::S3::Presigner.new(client: s3_client).presigned_url(
:put_object,
bucket: "my-bucket",
key: "uploads/example.pdf",
content_type: "application/pdf",
content_disposition: 'inline; filename="example.pdf"',
expires_in: 300
)The locally generated URL includes:
X-Amz-SignedHeaders=content-disposition;content-type;hostReturn that URL and its required headers from the signing endpoint:
{
url: "<presigned PUT URL>",
key: "uploads/example.pdf",
headers: {
"Content-Type": "application/pdf",
"Content-Disposition": 'inline; filename="example.pdf"',
},
}Configure Uppy to use the response:
import Uppy from '@uppy/core';
import AwsS3 from '@uppy/aws-s3';
const uppy = new Uppy().use(AwsS3, {
shouldUseMultipart: false,
async signRequest(request) {
const response = await fetch('/uploads/sign', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
});
if (!response.ok) throw new Error('Signing failed');
return response.json();
},
});
uppy.addFile({
name: 'example.pdf',
type: 'application/pdf',
data: new File(['test upload'], 'example.pdf', {
type: 'application/pdf',
}),
});
await uppy.upload();The file contents are arbitrary here; the issue concerns the signed request headers.
Expected behavior
A supported way to send the additional headers required by the presigned request. In v5, getUploadParameters could return upload headers.
For example, signRequest could accept a response shaped like { url, key?, headers? } and forward those headers to the S3 request, with defined handling for its internally generated Content-Type header.
Actual behavior
In the 6.1.0 source, S3mini.request() only reads url and key from the signing response. S3Client.xhr() sets Content-Type internally but does not receive additional headers. Returning headers from the signing endpoint therefore does not forward Content-Disposition to S3.
This prevents using the presigned request as issued. AWS requires the signed headers to match the headers sent by the client, so omitting Content-Disposition should result in signature validation failure. See AWS guidance on presigned URL signature mismatches.
Verification so far: source inspection and local URL generation with the AWS SDK for Ruby. I have not yet run an end-to-end reproduction against S3; the signature failure above is the expected consequence, not an observed S3 response.
Source: transloadit/uppy