7 minute read

Teaser

Motivation

In the projects I’ve worked on, setting up AWS CloudFront with an S3 bucket for a static website is basically the DevOps equivalent of making instant noodles — straightforward, and it just works. The Frontend team builds a shiny new Single Page Application (SPA), spits out some static files, and hands them over to me. My job? Toss those files into an S3 bucket, sprinkle in some CloudFront magic, slap on a custom domain, and voilà, the website is alive.

But then, someone always goes, “Hey, can we also upload files to the same bucket?” Sure, why not? Enter S3 Pre-signed URLs combined with CloudFront allowing PUT requests. It’s like handing out VIP tickets for file uploads. And guess what? It works like a charm, just as I hoped.

Now, let’s talk about S3 Transfer Acceleration—a.k.a. the feature I kind of remembered when cramming for my AWS SAA certification. Back then, it was one of those “just memorize it and move on” topics. And like most trivia from certification prep, it vanished from my brain the moment I passed the exam.

Fast forward to today. As I gear up for the AWS SAP certification, guess who’s back? Yep, that mysterious feature. This time, though, I want to actually understand it. No shortcuts, no “because the practice test said so.” Just good old-fashioned learning.

So, I dive into the documentation and read this:

Amazon S3 Transfer Acceleration is a bucket-level feature that enables fast, easy, and secure transfers of files over long distances between your client and an S3 bucket. Transfer Acceleration is designed to optimize transfer speeds from across the world into S3 buckets. It takes advantage of the globally distributed edge locations in Amazon CloudFront. As data arrives at an edge location, it is routed to Amazon S3 over an optimized network path.

Cool, right? But then a thought hits me: “Wait… is AWS just doing the same thing I’ve been doing with CloudFront and PUT requests? Or am I unknowingly reinventing the wheel here?”

So now I’m stuck in this existential crisis: Which is better? My clever CloudFront + S3 upload solution, or AWS’s S3 Transfer Acceleration sorcery?

Looks like I’ve got some digging to do. But hey, if nothing else, at least I’m getting my money’s worth from this certification prep.

Diagram

First things first, we need to prepare something that will help us stay on the right track - an infrastructure diagram.

diagram

Specification

  • CloudFront Distribution
    • Custom domain: enabled
    • Origin: S3
    • All other settings: default
  • S3 bucket
    • Type: General Purpose
    • Region: ap-southeast-1
    • All other settings: default

Setting Up

Create AWS S3 bucket

Let’s create AWS S3 bucket name bucket-for-transfer-acceleration-demo with default settings.

Figure 1

Done! Figure 2

Enable S3 Transfer Acceleration

Go to bucket-for-transfer-acceleration-demo > Properties > Transfer acceleration > Edit

Figure 3

Done! Figure 4

Create CloudFront ditribution

Create new CloudFront distribution with origin is S3 bucket created in previous step.

Figure 5

For Origin access, use Origin access control settings (recommended). Let’s create a new one.

Figure 6

For Allowed HTTP methods, select GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE.

Figure 7

Done enable WAF, I don’t want to lose my money :(

Figure 8

We will add custom domain later. Leave all other settings with default value, and Done.

Figure 9

Update S3 bucket policy to make OAC work

Remember to add s3:PutObject permission to allow upload file.

Figure 10

Create ACM certificate for CloudFront Distribution

Remember switch to us-east-1 region =)). Domain name is: s3-acc.skillsboost.cloud

Figure 11

Done!

Figure 12

Add CloudFront Alternative domain

Figure 13

Done! Figure 14

Prepare Javascript script to generate Presign URL

This script will create presigned url for 3 endpoint type:

  • CloudFront endpoint
  • Transfer Acceleration endpoint
  • S3 regional endpoint
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
import {fromIni} from "@aws-sdk/credential-providers";
import {HttpRequest} from "@smithy/protocol-http";
import {getSignedUrl, S3RequestPresigner} from "@aws-sdk/s3-request-presigner";
import {parseUrl} from "@smithy/url-parser";
import {formatUrl} from "@aws-sdk/util-format-url";
import {Hash} from "@smithy/hash-node";

// Generate a presigned URL for a custom endpoint
const region = "ap-southeast-1";
const url = parseUrl(`https://s3-acc.skillsboost.cloud/test.txt`);
const presigner = new S3RequestPresigner({
credentials: fromIni(),
region,
sha256: Hash.bind(null, "sha256"),
});

const signedUrlObject = await presigner.presign(
new HttpRequest({...url, method: "PUT"})
);
console.log("CloudFront endpoint:", formatUrl(signedUrlObject));
console.log();

// Generate a presigned URL for transfer acceleration endpoint
const client2 = new S3Client({useAccelerateEndpoint: true, region: region})
const command2 = new PutObjectCommand({ Bucket: "bucket-for-transfer-acceleration-demo", Key: "test2.txt"  });
const signedUrlObject2 = await getSignedUrl(client2, command2, { expiresIn: 3600 });
console.log("Acceleration endpoint:", signedUrlObject2);
console.log();



// Generate a presigned URL using default endpoint
const client3 = new S3Client({useAccelerateEndpoint: false, region: region})
const command3 = new PutObjectCommand({ Bucket: "bucket-for-transfer-acceleration-demo", Key: "test3.txt"  });
const signedUrlObject3 = await getSignedUrl(client3, command3, { expiresIn: 3600 });
console.log("Default endpoint:", signedUrlObject3);

Do the Test

This is the fun part!

DNS resolution testing

First, we will determine how many IP addresses we get when resolving the DNS records for the domains.
We will use the dig command to perform the DNS resolution.

CloudFront domain:

dig +noall +answer s3-acc.skillsboost.cloud

s3-acc.skillsboost.cloud. 300   IN      CNAME   d2vmtkxxkxgoqq.cloudfront.net.
d2vmtkxxkxgoqq.cloudfront.net. 60 IN    A       108.157.32.85
d2vmtkxxkxgoqq.cloudfront.net. 60 IN    A       108.157.32.56
d2vmtkxxkxgoqq.cloudfront.net. 60 IN    A       108.157.32.98
d2vmtkxxkxgoqq.cloudfront.net. 60 IN    A       108.157.32.89

Transfer Acceleration domain:

dig +noall +answer bucket-for-transfer-acceleration-demo.s3-accelerate.amazonaws.com

bucket-for-transfer-acceleration-demo.s3-accelerate.amazonaws.com. 60 IN A 3.162.57.183

S3 regional domain:

dig +noall +answer bucket-for-transfer-acceleration-demo.s3.ap-southeast-1.amazonaws.com

bucket-for-transfer-acceleration-demo.s3.ap-southeast-1.amazonaws.com. 60 IN CNAME s3-r-w.ap-southeast-1.amazonaws.com.
s3-r-w.ap-southeast-1.amazonaws.com. 2 IN A     52.219.164.210
s3-r-w.ap-southeast-1.amazonaws.com. 2 IN A     3.5.151.108
s3-r-w.ap-southeast-1.amazonaws.com. 2 IN A     3.5.149.130
s3-r-w.ap-southeast-1.amazonaws.com. 2 IN A     3.5.147.144
s3-r-w.ap-southeast-1.amazonaws.com. 2 IN A     3.5.146.102
s3-r-w.ap-southeast-1.amazonaws.com. 2 IN A     3.5.149.122
s3-r-w.ap-southeast-1.amazonaws.com. 2 IN A     3.5.150.166
s3-r-w.ap-southeast-1.amazonaws.com. 2 IN A     3.5.150.116

And the results are:

# Domain No. IP address(es)
1 s3-acc.skillsboost.cloud 4
2 bucket-for-transfer-acceleration-demo.s3-accelerate.amazonaws.com 1
3 bucket-for-transfer-acceleration-demo.s3.ap-southeast-1.amazonaws.com 8

More IP addresses mean more redundancy is added. However, there is no guarantee that the performance of all servers behind those IPs will be equal.

Let’s Ping it

Now, let’s ping the servers behind the domains 100 times, repeating the test 3 times for each domain. The results will depend on the network your laptop is currently connected to. My Access Point is close to me, so we can safely ignore the latency caused by my network.

CloudFront domain:

ping -c 100 s3-acc.skillsboost.cloud

#1
--- d2vmtkxxkxgoqq.cloudfront.net ping statistics ---
100 packets transmitted, 100 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 18.736/27.995/104.295/11.745 ms

#2
--- d2vmtkxxkxgoqq.cloudfront.net ping statistics ---
100 packets transmitted, 99 packets received, 1.0% packet loss
round-trip min/avg/max/stddev = 17.786/26.227/143.976/13.945 ms

#3
--- d2vmtkxxkxgoqq.cloudfront.net ping statistics ---
100 packets transmitted, 100 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 17.880/25.652/49.138/6.090 ms

Transfer Acceleration domain:

ping -c 100 bucket-for-transfer-acceleration-demo.s3-accelerate.amazonaws.com

#1
--- bucket-for-transfer-acceleration-demo.s3-accelerate.amazonaws.com ping statistics ---
100 packets transmitted, 99 packets received, 1.0% packet loss
round-trip min/avg/max/stddev = 18.010/26.608/73.290/8.460 ms

#2
--- bucket-for-transfer-acceleration-demo.s3-accelerate.amazonaws.com ping statistics ---
100 packets transmitted, 100 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 18.316/24.037/37.018/4.041 ms

#3
--- bucket-for-transfer-acceleration-demo.s3-accelerate.amazonaws.com ping statistics ---
100 packets transmitted, 100 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 18.213/27.100/153.949/14.144 ms

S3 regional domain:

ping -c 100 bucket-for-transfer-acceleration-demo.s3.ap-southeast-1.amazonaws.com

#1
--- s3-r-w.ap-southeast-1.amazonaws.com ping statistics ---
100 packets transmitted, 100 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 47.413/58.297/299.782/25.117 ms

#2
--- s3-r-w.ap-southeast-1.amazonaws.com ping statistics ---
100 packets transmitted, 100 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 45.509/56.717/165.572/17.402 ms

#3
--- s3-r-w.ap-southeast-1.amazonaws.com ping statistics ---
100 packets transmitted, 100 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 45.907/53.702/139.683/10.706 ms

And the results - calculated using average between 3 times.

# Domain Latency Min (ms) Latency Avg (ms) Latency Max (ms)
1 s3-acc.skillsboost.cloud 18.134 26.625 99.136
2 bucket-for-transfer-acceleration-demo.s3-accelerate.amazonaws.com 18.179 25.915 88.085
3 bucket-for-transfer-acceleration-demo.s3.ap-southeast-1.amazonaws.com 46.276 56.238 201.679

Since the domains s3-acc.skillsboost.cloud and bucket-for-transfer-acceleration-demo.s3-accelerate.amazonaws.com leverage edge locations, we benefit from the reduced distance to the server. As a result, the average latency is much lower compared to the domain of the regional endpoint. There is no noticeable difference between the CloudFront domain and the Transfer Acceleration domain.

Give it a big file

I’ll generate a 256Mb binary for testing using this command:

dd if=/dev/urandom of=256Mb.bin bs=64M count=4 iflag=fullblock

Then create presigned urls using script above to upload 256Mb test file.

node s3-presigned-url-generator.js

CloudFront endpoint: https://s3-acc.skillsboost.cloud/test.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIA3....%2F20241208%2Fap-southeast-1%2Fs3%2Faws4_request&X-Amz-Date=20241208T130513Z&X-Amz-Expires=900&X-Amz-Security-Token=IQoJ...yrV&X-Amz-Signature=5f4d0c9fda9d25d06&X-Amz-SignedHeaders=host

Acceleration endpoint: https://bucket-for-transfer-acceleration-demo.s3-accelerate.amazonaws.com/test2.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIA3R...%2F20241208%2Fap-southeast-1%2Fs3%2Faws4_request&X-Amz-Date=20241208T130514Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJ...%3D&X-Amz-Signature=cd916404d122704835e7911&X-Amz-SignedHeaders=host&x-id=PutObject

Default endpoint: https://bucket-for-transfer-acceleration-demo.s3.ap-southeast-1.amazonaws.com/test3.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIA3R...5%2F20241208%2Fap-southeast-1%2Fs3%2Faws4_request&X-Amz-Date=20241208T130515Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3...LI%3D&X-Amz-Signature=8e9c4b7456cd94edbad4cac8&X-Amz-SignedHeaders=host&x-id=PutObject

AWS CloudShell in us-east-1 region will be used to upload files using a pre-signed URL to simulate far away user. We will upload files 3 times for each pre-signed URL.

# CloudFront endpoint
time wget --no-check-certificate --quiet   --method PUT   --timeout=0   --header 'Content-Type: application/octet-stream'   --body-file='./256Mb.bin' 'https://s3-acc.skillsboost.cloud/test.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIA3....%2F20241208%2Fap-southeast-1%2Fs3%2Faws4_request&X-Amz-Date=20241208T130513Z&X-Amz-Expires=900&X-Amz-Security-Token=IQoJ...yrV&X-Amz-Signature=5f4d0c9fda9d25d06&X-Amz-SignedHeaders=host'

# Acceleration endpoint
time wget --no-check-certificate --quiet   --method PUT   --timeout=0   --header 'Content-Type: application/octet-stream'   --body-file='./256Mb.bin' 'https://bucket-for-transfer-acceleration-demo.s3-accelerate.amazonaws.com/test2.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIA3R...%2F20241208%2Fap-southeast-1%2Fs3%2Faws4_request&X-Amz-Date=20241208T130514Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJ...%3D&X-Amz-Signature=cd916404d122704835e7911&X-Amz-SignedHeaders=host&x-id=PutObject'

# Default endpoint
time wget --no-check-certificate --quiet   --method PUT   --timeout=0   --header 'Content-Type: application/octet-stream'   --body-file='./256Mb.bin' 'https://bucket-for-transfer-acceleration-demo.s3.ap-southeast-1.amazonaws.com/test3.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIA3R...5%2F20241208%2Fap-southeast-1%2Fs3%2Faws4_request&X-Amz-Date=20241208T130515Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3...LI%3D&X-Amz-Signature=8e9c4b7456cd94edbad4cac8&X-Amz-SignedHeaders=host&x-id=PutObject'

And the results:

# Domain Upload Time Avg (ms)
1 s3-acc.skillsboost.cloud 23.385
2 bucket-for-transfer-acceleration-demo.s3-accelerate.amazonaws.com 7.493
3 bucket-for-transfer-acceleration-demo.s3.ap-southeast-1.amazonaws.com 22.885

You know what? I’m a bit let down. Figure 15

Turns out, using the CloudFront endpoint for uploads is like trying to sprint in flip-flops—just doesn’t work as well as I’d hoped. It’s actually slower than the default endpoint. Ouch. But hey, here’s the good news: Transfer Acceleration absolutely zooms! It’s over three times faster when uploading files from us-east-1 to ap-southeast-1—basically halfway across the planet.

This speed boost isn’t magic (though it feels like it). Transfer Acceleration taps into Amazon CloudFront’s edge locations to slingshot your data across the globe. Science!

Meanwhile, poor CloudFront is sitting there going, “Uploads? That’s not really my thing. I’m more of a content delivery and caching kind of service.” Fair enough, CloudFront. Stay in your lane.

Moral of the story: when it comes to long-distance uploads, Transfer Acceleration is like the Concorde, while the other options are stuck in economy class.

Conclusion

AWS may have won this round, but don’t get too comfortable. I’ll be back for my revenge! 😏

Updated: