botocore icon indicating copy to clipboard operation
botocore copied to clipboard

Missing `response-content-disposition` response header in CloudFront requests

Open git-hyagi opened this issue 1 month ago • 1 comments

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

git-hyagi avatar Dec 04 '25 18:12 git-hyagi

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

adev-code avatar Dec 15 '25 21:12 adev-code

Hi @adev-code!

Thank you for taking a look into this issue. Sure, I'll wait for updates from your side then 👍

git-hyagi avatar Dec 20 '25 17:12 git-hyagi