This blog post is part of a series of posts about how to deploy a Django app to AWS. If you haven't deployed your Django app to EC2 and S3 yet, I recommend reading the AWS EC2 and AWS S3 posts first.
In the current setup, our static and media files are being served by S3, but any users living very far away may experience a noticeable delay when loading these files. This is because our S3 bucket is attempting to serve users all across the world, which can be very slow. Instead, we can use a content delivery network (CDN) to cache our static and user-uploaded files in a global network of edge locations so they can be accessed much more quickly. Although many CDNs exist, such as CloudFlare, for this blog post we'll use AWS CloudFront and configure our Django app to use it.
Let's get started by creating a new CloudFront distribution.
- Navigate to the AWS CloudFront console and select Create distribution.
- Under Origin domain, select the S3 bucket you had created previously.
- Under Origin access, select Legacy access identities and select Create new OAI. Then select Yes, update the bucket policy.
- Under Allowed HTTP methods, select GET, HEAD
- Under Web Application Firewall, select Do not enable security protections.
- Under Response headers policy, select SimpleCORS.
- Finally, select Create Distribution.
Great! Now our CloudFront distribution is created. Note the distribution domain name, which looks like https://<name>.cloudfront.net
, since we'll need it later. Next, we need to modify our S3 bucket to allow CloudFront to access it.
- Navigate to the AWS S3 console, and select the bucket we created in the previous blog post.
- In the Permissions tab, edit the public access setting to Block public access. We no longer want to allow the public to access our S3 bucket directly—instead, only Cloudfront will access our bucket and periodically cache the files that users request.
- Keep ACL disabled.
- In the Policy tab, add the following policy:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowCloudFrontServicePrincipal", "Effect": "Allow", "Principal": { "Service": "cloudfront.amazonaws.com" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::procurespark/*", "Condition": { "StringEquals": { "AWS:SourceArn": "arn:aws:cloudfront::794311998140:distribution/E1WA3LKALKX6RF" } } } ] }
Finally, let's configure Django to point to our new CloudFront distribution.
- In
settings.py
, modify theAWS_S3_CUSTOM_DOMAIN
setting to use the CloudFront distribution domain name:AWS_S3_CUSTOM_DOMAIN = '<name>.cloudfront.net'
Now our static and media files will be served from CloudFront, which should be much faster for users living far away.
Note that whenever you push new static files to S3, CloudFront will continue serving the old files for some time. To force CloudFront to grab the latest files from S3, you'll need to invalidate the cache for CloudFront manually:
- Navigate to the AWS CloudFront console and select the distribution you just created.
- Under the Invalidations tab, select Create invalidation.
- In the Paths field, enter
/*
and select Create. After a few minutes, the latest static files should be getting served on your website.
Since this process can quickly become tedious if you're updating static files (like JavaScript) frequently, you can also enable Django's built-in cache-busting behavior. This will automatically add a unique hash to the end of your static files, which will force CloudFront to fetch the latest version of each file.
Although cache-busting is built natively into Django, we need to make a small modification to make it work with S3:
- Create a new file called
storage.py
in the your primary app directory and add the following content:from storages.backends.s3boto3 import S3Boto3Storage from django.contrib.staticfiles.storage import ManifestFilesMixin class ManifestS3Storage(ManifestFilesMixin, S3Boto3Storage): """ A class for implementing storage on S3 with hashes appended. """ pass
- In
settings.py
, modify theSTATICFILES_STORAGE
setting to point to the new storage class we just created:STATICFILES_STORAGE = 'your_app.storage.ManifestS3Storage'
Great! Now whenever you run python manage.py collectstatic
, your static files will be uploaded to S3 with a unique hash appended to each file name. This will force CloudFront to fetch the latest version of each static file from S3, so we don't have to manually invalidate the cache every time. Our static files are ready for scaling worldwide!