Hosting Django Static Files in AWS using S3 and CloudFront: A Comprehensive Guide
Tags: webdev, programming, tutorial, python
Published: 2023-10-17 07:48:50

In today’s digital landscape, having a reliable and scalable infrastructure for hosting static files is crucial for web applications. Django, a popular Python web framework, offers seamless integration with Amazon Web Services (AWS) to accomplish this task. In this guide, we will explore how to host Django static files in AWS using CloudFront, a powerful content delivery network (CDN) service, to ensure high availability and fast content delivery.

*The article is a step-by-step guide on how to achieve the goal, but nevertheless, I assume that the reader has basic knowledge of Django and AWS.*

![Photo by [Faisal](https://unsplash.com/@faisaldada?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)](https://cdn-images-1.medium.com/max/8064/0*GVZKEfjN8MRp7JCN)
<br>
## Understanding Django Static Files

Before diving into the technical aspects, it’s important to understand Django static files. These files include CSS stylesheets, JavaScript scripts, images, and other assets that are served directly by the web server without any processing. Managing static files efficiently enhances the overall performance and user experience of your Django application.

Normally, when you deploy a service into production using Django, especially when you put the settings variable DEBUG=False Django excepts you to provide a way to handle these static files like a cloud service or CDN. What happens in this flow it’s that whenever the service wants to access those files, they’re fetched from the CDN or a cloud service instead of living in the same codebase as the web service itself.

For this article, we are going to use AWS Services called **S3** and **CloudFront**.

* **Amazon S3** is a highly scalable and durable cloud storage service provided by Amazon Web Services (AWS). It allows you to store and retrieve large amounts of data, such as images, videos, documents, and backups, in a secure and reliable manner. S3 provides an object-based storage model, where each object is stored in a bucket and accessed using a unique key.

* **Amazon CloudFront** is a content delivery network (**CDN**) also provided by Amazon Web Services. It helps deliver content, such as web pages, images, videos, and other static or dynamic files, to users with low latency and high transfer speeds. CloudFront caches the content at edge locations, which are distributed globally, closer to the users, reducing the distance and network latency. This improves the performance of delivering content to users across different regions.

<br>
<br>
### Setting up IAM User

Login with your root user and then continue to create a new user that you will use during this exercise. You’ll need to create a user that has access to 
S3 and CloudFront:

* Navigate to **IAM > Users > Add New User**

* Enter the username you want for your user and in the next step, select **Attach policies directly** tab.

* Attach these two policies: [CloudFrontFullAccess](https://us-east-1.console.aws.amazon.com/iamv2/home?region=us-east-1#/policies/details/arn%3Aaws%3Aiam%3A%3Aaws%3Apolicy%2FCloudFrontFullAccess) and [AmazonS3FullAccess](https://us-east-1.console.aws.amazon.com/iamv2/home?region=us-east-1#/policies/details/arn%3Aaws%3Aiam%3A%3Aaws%3Apolicy%2FAmazonS3FullAccess)

Or if you want to skip this part (for simplicity), just use the root user **(but this is not recommended in any case for the sake of security)**
<br>
### Setup S3 and CloudFront

First, we need the S3 bucket on which we are going to upload our static files. Navigate to the **[S3 console](https://s3.console.aws.amazon.com/)** and click > **Create Bucket**

* Enter a name for the bucket and select the region

* Keep ACL-s disabled

* Tick **Block all public access** and leave everything else as default

You’re good to go!

Now let’s set up CloudFront for the newly created Bucket.
Since we kept everything private in our bucket, we need to let CloudFront access some of the folders that exist in our bucket.

Navigate to the CloudFront console and click **Create a CloudFront distribution**. After that, you might need to fill up some information:

* Origin Domain: Choose the S3 Bucket that you created

* Origin Access: Choose **Legacy access identities** and then click Create new OAI

* On the bucket policy select: **Yes, update the bucket policy**

* Allowed HTTP methods: **GET, HEAD, OPTIONS**

* Web Application Firewall (WAF): Do not enable security protections

* Response headers policy **SimpleCORS**

That’s it, keep everything else as default. Now a new CloudFront distribution will be created and will return back a **Distribution domain name** which in my case looks something like this:

    https://d29u7vv9xp7q8y.cloudfront.net

Great, now let’s make the necessary changes in our Django application.
<br>
### Setting up our Django application — Uploading static files

In order to upload our static files to the AWS S3 bucket, we need to turn off **DEBUG** mode and also update some settings and configurations in our Django application. To start off, first, install django-storageslibrary which is a library to handle storages better in Django.

    $ pip install django-storages

With that done, create a new Python module named storage_backends.py and add the following code:

    from django.conf import settings
    from storages.backends.s3boto3 import S3Boto3Storage
    
    
    class StaticStorage(S3Boto3Storage):
        location = 'static'
        custom_domain = settings.CLOUDFRONT_DOMAIN

This will tell that you want to upload static files to a folder named static and the custom domain to handle and redirect the static files would be the **CLOUDFRONT_DOMAIN** which is set up in settings.py
>  ***NOTE: to not hard-code the environment variables as below and use a mechanism to manage the environment variables. A simple one would be os.getenv().***

Modify the settings and add the following environment variables:

    DEBUG = False
    AWS_ACCESS_KEY_ID = 'YOUR_AWS_ACCESS_KEY_ID'
    AWS_SECRET_ACCESS_KEY = 'YOUR_AWS_SECRET_ACCESS_KEY'
    AWS_STORAGE_BUCKET_NAME = 'NAME_OF_BUCKET'
    AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com'
    AWS_S3_REGION_NAME = "YOUR_LOCATION_IN_AWS"
    AWS_S3_SIGNATURE_VERSION = "s3v4"
    AWS_QUERYSTRING_EXPIRE = 604800
    CLOUDFRONT_DOMAIN = 'YOUR_CLOUD_FRONT.cloudfront.net'
    
    STATIC_LOCATION = "static"
    STATIC_URL = f'{CLOUDFRONT_DOMAIN}/static/'
    # Add your path in the STATICFILES_STORAGE
    STATICFILES_STORAGE = 'django_static.storage_backends.StaticStorage'

This should be enough. Now before you start your Django application, open a new console and collect the static files:

    python manage.py collectstatic
    
    You have requested to collect static files at the destination
    location as specified in your settings.
    
    This will overwrite existing files!
    Are you sure you want to do this?
    
    Type 'yes' to continue, or 'no' to cancel: yes
    
    125 static files copied.
    

Now navigate to your AWS account go to your bucket and see if the files are copied to a folder named static , if they’re, everything worked as expected.
<br>
### Testing CloudFront Integration:

Now it’s time to test whether CloudFront is serving your static files correctly. Start your Django development server and access your application. Inspect the network requests using your browser’s developer tools to verify that the static files are being fetched from the CloudFront URL you specified.

    python manage.py runserver    
    Performing system checks...
    
    System check identified no issues (0 silenced).
    July 30, 2023 - 16:58:13
    Django version 4.2.3, using settings 'django_static.settings'
    Starting development server at http://127.0.0.1:8000/
    Quit the server with CONTROL-C.

Now visit localhost:8000/admin or 127.0.0.1/admin and open developer tools, for example in Google Chrome: CTRL+SHIFT+I and go to the network tab. Click one of the .css files and navigate to Headers to see the requested URL and see if it’s pointing to the CloudFront URL that you defined in **settings.py**.

![Inspecting with Developer Tools](https://cdn-images-1.medium.com/max/3532/1*N_JSdwapiulhhoHex040SA.png)
<br>
### Enabling CloudFront Cache Invalidation:

To ensure that your users receive the latest versions of your static files, configure CloudFront cache invalidation. There are two common approaches to achieving this. You can manually invalidate the CloudFront cache whenever you update your static files by using the AWS Management Console or AWS CLI. Alternatively, you can implement cache invalidation techniques in your Django application, such as versioning static files or appending a query string parameter.
<br>
### Wrapping up

In this article, we have set up a Python application with Django & Deploy and serve the static files from CloudFront.

**You can find the full source code of the article on the GitHub repository, with the instructions.**

[www.github.com/vjanz/django-static-files-s3-cloudfront](http://www.github.com/vjanz/django-static-files-s3-cloudfront)

If you found it helpful, please don’t forget to clap & share it on your social network or with your friends.

**If you want to support my work, you can buy me a coffee by clicking the image below 😄**



[![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ltkzluzpxchzvl208u7q.gif)](https://www.buymeacoffee.com/valonjanuzaj)

If you have any questions, feel free to reach out to me.

Connect with me on [LinkedIn](https://www.linkedin.com/in/valon-januzaj-b02692187/), [GitHub](http://www.github.com/vjanz)