Missing `response-content-disposition` response header in CloudFront requests
Describe the bug
When generating presigned URLs for CloudFront distributions, query parameters are not being transformed from their API parameter format (ResponseContentDisposition) to HTTP query string format (response-content-disposition).
Regression Issue
- [ ] Select this option if this issue appears to be a regression.
Expected Behavior
When calling generate_presigned_url() (from CloudFrontSigner) method with a URL containing query parameters like ResponseContentDisposition, the parameter should be transformed to its HTTP query string format (response-content-disposition) to match the S3 API specification defined in the service model at botocore/data/s3/2006-03-01/service-2.json:
"ResponseContentDisposition":{
"shape":"ResponseContentDisposition",
"documentation":"<p>Sets the <code>Content-Disposition</code> header of the response.</p>",
"location":"querystring",
"locationName":"response-content-disposition"
}
Current Behavior
The generate_presigned_url() method does not perform any query parameter transformation, resulting in URLs with incorrectly formatted query parameters and not returning a response with the response-content-disposition header.
Reproduction Steps
import os
import boto3
from botocore.signers import CloudFrontSigner
from datetime import datetime, timedelta
# Using CloudFront Signer
key_id="1234567"
rsa_signer = lambda message: b'signed'
cloudfront_signer = CloudFrontSigner(key_id, rsa_signer)
url_with_params = "https://test.cloudfront.net/myfile.pdf?ResponseContentDisposition=attachment%3Bfilename%3D%22download.pdf%22"
url = cloudfront_signer.generate_presigned_url(
url_with_params,
date_less_than=datetime.now() + timedelta(hours=1)
)
print(f"CLOUDFRONT: {url}")
# Using S3 Client (for comparison)
os.environ['AWS_ACCESS_KEY_ID'] = 'testing'
os.environ['AWS_SECRET_ACCESS_KEY'] = 'testing'
s3_client = boto3.client('s3')
url = s3_client.generate_presigned_url(
'get_object',
Params={
'Bucket': 'mybucket',
'Key': 'myfile.pdf',
'ResponseContentDisposition': 'attachment; filename="download.pdf"'
},
ExpiresIn=3600
)
print(f"S3: {url}")
output:
CLOUDFRONT: https://test.cloudfront.net/myfile.pdf?ResponseContentDisposition=attachment%3Bfilename%3D%22download.pdf%22&Expires=1764865633&Signature=c2lnbmVk&Key-Pair-Id=1234567
S3: https://mybucket.s3.amazonaws.com/myfile.pdf?response-content-disposition=attachment%3B%20filename%3D%22download.pdf%22&AWSAccessKeyId=testing&Signature=YCR4VzzJ2QzItrKyL4Q6%2F89nxuw%3D&Expires=1764876433
Possible Solution
No response
Additional Information/Context
Our project uses django-storages and for requests with s3, the response-content-disposition header is returning as expected in:
https://github.com/jschneier/django-storages/blob/ca89a94a7462a2423df460e7bfd5f847457042ca/storages/backends/s3.py#L697-L699
but with CloudFront, this part https://github.com/jschneier/django-storages/blob/ca89a94a7462a2423df460e7bfd5f847457042ca/storages/backends/s3.py#L685-L687 is not returning the header and the query parameter is also not updated.
We believe this is an issue in botocore because in s3 https://github.com/boto/botocore/blob/4e6ef4383681dee603e8aa53fa7a7a89344732be/botocore/signers.py#L756 returns the query parameter with the expected format, while with cloudfront https://github.com/boto/botocore/blob/4e6ef4383681dee603e8aa53fa7a7a89344732be/botocore/signers.py#L407 the query parameter is not modified .
SDK version used
1.42.2
Environment details (OS name and version, etc.)
Red Hat Enterprise Linux release 9.7
Hello @git-hyagi, thanks for reaching out. Service model helps shape the request URL. Looking at the difference between S3 and CF service models, CF does not have content-disposition and S3 has. Looking at your repro code, it uses GetObjectRequest operation and has content disposition in its model.
I have reached out to CloudFront Service team if this header can be added to their model. I have no timeline yet for this request. I will update if they have any updates from their side.
Internal Ref: P354852258
Hi @adev-code!
Thank you for taking a look into this issue. Sure, I'll wait for updates from your side then 👍