IAN WALDRON IAN WALDRON

How To Test A Domain Locally

This article explores how to test your Django application using a domain rather than "localhost." Additionally, we'll look at a DNS cacheing issue you're likely to encounter and what to do about it.
June 9, 2024

Background

While working on your Django app in a development environment, you'll likely use the development server packaged along with the Django framework. When you start the server, a log will appear in the console showing your site is available at 127.0.0.1:8000, otherwise known as the loopback address which is your computer's way of referring to itself. Be default, your development server is made available on port 8000.

But what if you want to use your actual domain name to call your dev server? For example, let's use "example.com." While using our development server, we want to test our application with its actual domain name like example.com:8000 instead of using the loopback address. Fortunately this is an easy thing to do.

Update Hosts

In order to implement a custom domain name in a testing environment, we need to first update our machine's hosts file.

On Mac or linux this is accomplished with a quick sudo nano to /etc/hosts. You need to use sudo (i.e. run the command as root) or your changes to the file won't be committed. You'll be able to open the file and edit text, but when you go to exit and save your changes (ctrl + "x"), you receive an alert that the changes can't be written to the file due to permissions.

This will look something like:


##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##

127.0.0.1    example.com
127.0.0.1.   localhost
255.255.255.255  broadcasthost
::1.   localhost

On Windows, the process is similar. You'll use notepad to modify the hosts file but you'll need to have administrator privileges. In order to do so, instead of left-clicking the notepad icon to start the application and edit a text file, right-click and select Run as administrator. Then, make your changes and save the file. The file is located at %WinDir%\System32\Drivers\Etc.

Issues

Two problems I've encountered when testing locally involve DNS and certificate caching. 

DNS Cache

When you search for a domain name in your browser for the first time, a DNS lookup will occur. Your browser will look externally to DNS servers to determine how to resolve the domain name to an IP address. This DNS query takes time and slows the user experience.

So instead your machine will cache (i.e. store locally) the domain's DNS information for a period of time so that the DNS query doesn't need to occur with each request to the domain. Your machine will check its own records and only look externally if the search is unsuccessful or the data has expired (see TTL).

DNS information can be cached in a number of different locations on your machine. But primarily you'll need to know how to work with your operating system's cache in memory and your browser's cache.

With Mac and if you're using Safari, then all you need is sudo killall -HUP mDNSResponder because Safari relies on system cache. This would be executed from a terminal instance. In Windows, open up an instance of Command Prompt and the command to remove DNS cache is ipconfig /flushdns.

Chrome caches DNS on its own, so if you're using Chrome you'll need to flush its DNS cache as well. This can be accomplished by navigating to chrome://net-internals/#dns from a Chrome browser instance. Then, click "Clear host cache."

Screenshot of chrome browser showing where to clear cache

Certificate Cache

Another problem you'll encounter involves certificates. If you've already navigated to the external location of the domain you're trying to test, then your browser likely has cached the certificate the site is using for HTTPS traffic. This is an issue because your browser will try to force the HTTPS connection but your Django development server only supports HTTP traffic. This will result in a 400 error.

app-1  | [09/Jun/2024 00:48:24] code 400, message Bad request version.
app-1  | [09/Jun/2024 00:48:24] You're accessing the development server over HTTPS, but it only supports HTTP.

To correct for this in Safari, you'll need to clear browser cache. This can be accomplished by navigating to Safari > Settings > Privacy then select "Manage Website Data" and select "Remove" with the desired site selected or select "Remove All" to dump the entire cache. 

With that, you'll now be able to direct traffic locally to your development server using your custom domain saved to etc/hosts. Be sure to append the port number preceded by a colon to the domain. For example, "example.com:8000" (if you're running on port 8000). Finally, if you're still having issues, a quick restart of the browser never hurt anyone.

Simplify Your Life

If you're using the same machine for testing that you also use to routinely access the live, external site, then this process might become annoying after a while. Instead, why don't we use a subdomain for testing locally?

For my purposes, I'm choosing to use the convention "local.example.com." Choose a subdomain that fits your needs and makes sense to you. First, update your /etc/hosts file for this domain.

##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##

127.0.0.1    local.example.com
127.0.0.1.   localhost
255.255.255.255  broadcasthost
::1.   localhost

Unless this subdomain has already been accessed from your browser, you shouldn't need to follow the earlier steps of clearing various cache. But if you run into problems, that's a good place to start.

Then, all you need to do is update your Django application to allow traffic from that subdomain. I only want to allow traffic from the subdomain in testing so I'm going to check the Django DEBUG setting and append ALLOWED_HOSTS with our testing location if True.

# settings.py

ALLOWED_HOSTS = os.getenv('DJANGO_ALLOWED_HOSTS', '').split(',')
if DEBUG:
    ALLOWED_HOSTS += ['local.example.com',]

Here in my settings file, I first retrieve my ALLOWED_HOSTS from an environment variable. Then, I append my testing variant to the list if DEBUG is True.

Now I'll be able to access both my development server as well as the external resource without one interfering with the other. I can even access them both simultaneously for comparison if need be because the resources exist at different locations.

Final Thoughts

Testing your Django application with a custom domain is a common concern when working on a project and especially if your project is built with the intention of using multiple domains. My previous article How to Handle Multiple Domains in Django with the Sites Framework goes into just such an example.

This can be easily accomplished by adjusting your machine's hosts/etc file to direct traffic locally and to your development server. Remember that there are issues with cache that can occur if you're not careful. Last, you can update your configuration to work locally with a subdomain so caching isn't an issue. Just remember to update your project accordingly.