Guide: Serving PDFs to users from Amazon S3

ND
Last updated 2 months ago

Learn how to share PDFs that have been uploaded to S3 with your users.

Once your PDFs are generated and uploaded to S3, there are a number of ways you can provide them to your customers. We're going to walk through two common methods used to deliver files to users from S3. We’ll also take a look at how you can serve files from a custom domain name and how to implement a content distribution network (CDN) to speed up downloads. Finally, this guide looks at some security considerations when providing files to users from S3.

Option 1 - Making PDFs directly downloadable from S3

If you’d like your users to be able to download PDFs directly from your S3 bucket, you can make the bucket publicly accessible.

First, check the "Public Access Settings" for your bucket. The following two options must be set to False:

  • "Block new public bucket policies"

  • "Block public and cross-account access if bucket has public policies"

Bucket with correct public access settings

Next, you’ll need to modify the bucket policy that was created when you created the bucket.

Go to "Bucket Policy" under "Permissions", and add the following code to the statement list in the policy:

Remember to change myapp-bucket-name to the name of the bucket you're using.

{
"Sid":"PublicReadGetObject",
"Effect":"Allow",
"Principal": "*",
"Action":["s3:GetObject"],
"Resource":["arn:aws:s3:::myapp-bucket-name/*.pdf"]
}

Here’s an example of a full bucket policy which includes the statement above:

{
"Id": "PaperplanePolicy",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowPaperplaneAccess",
"Action": [
"s3:PutObject"
],
"Effect": "Allow",
"Resource": "arn:aws:s3:::myapp-bucket-name/*",
"Principal": {
"AWS": [
"arn:aws:iam::485347624719:user/s3-upload"
]
}
},
{
"Sid":"PublicReadGetObject",
"Effect":"Allow",
"Principal": "*",
"Action":["s3:GetObject"],
"Resource":["arn:aws:s3:::myapp-bucket-name/*.pdf"]
}
]
}

Once you’ve updated the policy to include the PublicReadGetObject statement, all files in the bucket will be accessible to the outside world.

To construct the URL to download a PDF, you’ll need to use the following format:

https://s3-region-code.amazonaws.com/bucket-name/job-id.pdf

For example, if your region is us-west-2 and your bucket name is myapp-bucket-name, then a URL might look like:

https://s3-us-west-2.amazonaws.com/myapp-bucket-name/10c67218-e605-4f3f-be0d-973ebaef4c85.pdf

Using this method, your users can download a file using the link as many times as they like whilst the file still exists on S3. If this isn’t desirable for your app, you may prefer to generate pre-signed URLs instead. Generating pre-signed URLs is described below.

Forcing downloads to use HTTPS

You may want to prevent downloads from occurring over unencrypted connections, particularly if your PDFs contain any sensitive data. This can be done by adding the following additional statement to your bucket policy:

{
"Sid": "PublicReadGetObject",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::myapp-bucket-name/*.pdf",
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
}
}
}

Option 2 - Generating a pre-signed URL

By generating pre-signed URLs you can control access to your PDFs more closely. The URL will automatically expire after a period of time you specify.

The article “Share an Object with Others” from AWS shows how to generate pre-signed URLs using the AWS SDK for Java and .NET.

You can also generate pre-signed URLs in any other language that the AWS SDK is available for, such as Node.js, PHP, Python or Ruby.

Generating a pre-signed URL in Ruby

Here’s an example of using the AWS SDK for Ruby to generate pre-signed URLs that expire after one hour (3600 seconds):

require "aws-sdk-s3"
s3 = Aws::S3::Resource.new
bucket = s3.bucket("myapp-bucket-name")
object = bucket.object("10c67218-e605-4f3f-be0d-973ebaef4c85.pdf")
url = object.presigned_url(:get, expires_in: 3600)

Serving PDFs over the Cloudfront CDN

To speed up downloads you might want to using the Cloudfront content distribution network to serve downloads from your S3 bucket.

Using the AWS web console, navigate to the Cloudfront service and create a new web distribution. Set the origin domain name to your S3 bucket (it should appear in the auto-complete list).

To enforce the use of HTTPS you may want to set “Viewer Protocol Policy” to “Redirect HTTP to HTTPS”. All other default settings can be left unchanged.

Once the distribution has been created (this usually takes a few minutes) you’ll be able to download PDFs over Cloudfront by constructing a URL using your Cloudfront distribution domain name:

https://d2tb8ht0iu22sg.cloudfront.net/10c67218-e605-4f3f-be0d-973ebaef4c85.pdf

Setting up a custom domain name for your downloads

If you’ve set up Cloudfront, it’s relatively straightforward to use your own custom domain name instead of the default cloudfront.net domain. To do this you should have prior experience managing DNS records and provisioning SSL certificates.

AWS have a comprehensive guide available, but a quick summary of the process is:

  • Use the AWS certificate manager to request an SSL certificate for your custom domain.

  • Validate the domain by adding a DNS record as instructed by the AWS certificate manager.

  • Add a CNAME DNS record pointing to your Cloudfront distribution domain name.

  • Once the SSL certificate has been validated, edit the Cloudfront distribution settings to add the new alternate domain name using the certificate.