CloudFront Over S3 [CDN]

CloudFront Over S3 [CDN]


Setting this thing up was one of those situations in which everything goes bad, stumbling from one hole to another. You need to sync Nginx, WordPress, S3 and CloudFront, so not an easy task. The order of things depends on what you already have and what you want to achieve. Anyway, here are some of the notes for the future reference when it comes to CloudFront Over S3.

Create AWS S3 Bucket

We’re not going to go into details here, it’s relatively straightforward process.

Go to AWS services -> S3 and hit “Create bucket”. Fill the fields in “Name and region”, leave other options as default ( “Configure options” & “Set permissions” tabs).
CloudFront Over S3 [CDN]: S3 Properties
S3 Properties

You might be required to set “Bucket policy” (or it’s implicitly added by IAM later on):

     "Version": "2008-10-17",
     "Id": "PolicyForCloudFrontPrivateContent",
     "Statement": [
             "Sid": "1",
             "Effect": "Allow",
             "Principal": {
                 "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity E3EPMARLA7L1EE"
             "Action": "s3:GetObject",
             "Resource": "arn:aws:s3:::<YOUR_DOMAIN>/*"

and CORS configuration:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="">

Another thing you’ll most likely need (for Total Cache CDN config) is a user with specific S3/CloudFront permissions.

Go to AWS IAM and create/add a user with AmazonS3FullAccess & CloudFrontFullAccess or CloudFrontReadOnlyAccess.

S3 and NGINX

You might encounter some problems if you don’t have properlly set CORS on your webserver:

add_header Access-Control-Allow-Methods "GET, POST";
add_header Access-Control-Allow-Origin *;

CloudFront over S3 with TotalCache [WordPress Plugin]

First, go to WordPress dashboard -> Performance -> General Settings. Select CDN and set CDN type to “Amazon CloudFront Over S3”.
Then, jump to WordPress dashboard -> Performance -> CDN and fill the necessary fields (Access/Secret key, Bucket, etc).
Hit “Test S3 upload & Cloudfront distribution”. You should see “Test passed”.

In “General” tab above, there are options on what to upload. You might be required to upload first set of files manually. Afterwards, it’s going to continue its course on its own (TotalCache). To exclude specific pages from caching, go to Performance -> "Page Cache" and insert them in “Never cache the following pages”

Note: You might encounter the issue of “No minify files generated” (W3TC), maybe explicitly tied to NGinx. Checking the Total Cache folder “/cache/minify/” you’ll see that the folder is empty. The solution, visit “/wp-admin/admin.php?page=w3tc_install” and copy the “Rewrite Rules” to nginx config (/etc/nginx/sites-available/<domain>, right below server/root tags). You can probably use “include <path>” but we’re playing it safe. Go to Performance -> Minify and enable “Rewrite URL Structure”. You might notice that the minify folder is still empty. Go to Performance -> CDN and hit the Upload minified files button under “General” section (Host Minified CSS & JS files). Don’t forget to save & purge or purge the current page. Mind your steps with JS/CSS configuration in Performance -> Minify section (JS – combine only/async and being careful on the position head/after body)

Below, in the “Advanced” tab, you’ll manage available options to your preference (e.g. “Add CORS header”, “Automatically upload minify files” and “Export changed files automatically”.

You might also be required to add some directories to the “Custom file list”. For example, *.ttf and/or *.woff were situated in a custom theme directory:

Note: Couple of other issues you can encounter are font related problems (ttf, woff, woff2) and also “Expiration headers” when it comes pictures.

You can try adjusting NGinx headers, adding expires sections for individual types:

location ~*  .(jpg|jpeg|png|gif|ico|css|woff2|font.css)$ {
     expires 7d;
     add_header Pragma public;
     add_header Cache-Control "public";

location ~ .(eot|ttf|otf|woff|woff2|font.css)$ {
     add_header Access-Control-Allow-Origin *;

But that probably won’t help.

Go to your S3, find the pictures that have problems with expiration and select Actions -> Change metadata. Click Add Metadata and set a few fields:
Cache-Control: max-age=86400
Expires: access plus 7 days

Next, you’ll need to invalidate those resources on CloudFront side for changes to take effect.

Go to AWS CloudFront -> Distribution Settings -> Invalidations. There, click the “Create invalidation” button and simply enter URLs you want to invalidate/refresh.
CloudFront Over S3 [CDN]: S3 Metadata
S3 Metadata

CloudFront Distribution

It’s probably best if we simply share some details of the configuration parameters from AWS CloudFront you can use. You can continue without SSL Certificate (use default) and subdomain (e.g. cdn.<DOMAIN>) configuration. Instead of having in front of our static resources, we usually want to set specific subdomain (cdn).

General config:

Alternate Domain Names (CNAMEs): cdn.<DOMAIN>
SSL Certificate: *.<DOMAIN>
Domain Name:
Custom SSL Client Support: Clients that Support Server Name Indication (SNI) - (Recommended)
Security Policy: TLSv1.1_2016
Supported HTTP Versions: HTTP/2, HTTP/1.1, HTTP/1.0
IPv6: Enabled

Origin config:

Origin Domain Name: <your s3 bucket - dropdown>
Origin path: empty
OriginID: <s3 id, implicitly set>
Restrict Bucket Access: No
Origin Custom Headers: None added

Behaviour config:

Path Pattern: Default (*)        
Origin or Origin Group: 
Viewer Protocol Policy: Redirect HTTP to HTTPS
Allowed HTTP Methods: GET, HEAD
Field-level Encryption Config: Empty
Cached HTTP Methods: GET, HEAD (Cached by default)
Cache Based on Selected Request Headers: None (Improves Caching)
Object Caching: Use Origin Cache Headers
Minimum TTL: 0
Maximum TTL: 31536000
Default TTL: 86400
Forward Cookies: None (Improves Caching)
Query String Forwarding and Caching: None (Improves Caching)
Smooth Streaming: No
Restrict Viewer Access: No
Compress Objects Automatically: Yes

You’ll probably have to experiment a bit, but these might help as a reference.

AWS CloudFront Certificate Manager

Instead of setting specific subdomain, we wanted to have an option for additional subdomains, so we used asterisk (*.<YOUR DOMAIN>).

After you click “request certificate” and fill the fields, AWS is going to require of you to add CNAME record to your DNS configuration (validation purposes):    CNAME 

Amazon will automatically/occasionally check DNS and validate your domain. From our experience, it’s relatively fast, 10-15 min.

With this being ready, you can continue to “General” distribution settings, setting up “SSL Cert” and “Alternative Domain Name”.


You’re probably going to hit some obstacles, problems, and things won’t work as expected . Is it worth it? Yes, we think so. When everything gets finished, when suffering and hard labor passes, things are not bad. We used a number of CDNs and CloudFront seem to provide a better performance and stability. For comparement, CloudFlare is showing a number of issues when it comes to stability (10-30 min of downtime monthly or sometime weekly).