How to Solve "The domain name provided is not valid according to RFC 1034/1035" in Django during Development

·

5 min read

With the django.middleware.common.CommonMiddleware middleware enabled in Django settings, attempting to access your django app from a url that looks something like blue-forest.ide.company.io (often used by online IDEs) or any other url not valid according to the RFC 1034/1035 rules will likely produce this error

Screen Shot 2021-02-18 at 10.42.59 AM.png

Even with the site name added to ALLOWED_HOSTS in settings.py or ALLOWED_HOSTS set to ['*'] to match any and every site, this error might still persist.

This is because, on every request, the django common middleware calls a get_host function that performs some validations on the hostname. Taking a peek at django.http.request.py, we can see how this function looks

    def get_host(self):
        """Return the HTTP host using the environment or request headers."""
        host = self._get_raw_host()

        # Allow variants of localhost if ALLOWED_HOSTS is empty and DEBUG=True.
        allowed_hosts = settings.ALLOWED_HOSTS
        if settings.DEBUG and not allowed_hosts:
            allowed_hosts = ['.localhost', '127.0.0.1', '[::1]']

        domain, port = split_domain_port(host)
        if domain and validate_host(domain, allowed_hosts):
            return host
        else:
            msg = "Invalid HTTP_HOST header: %r." % host
            if domain:
                msg += " You may need to add %r to ALLOWED_HOSTS." % domain
            else:
                msg += " The domain name provided is not valid according to RFC 1034/1035."
            raise DisallowedHost(msg)

It's clear that the error we're seeing happens if no domain is returned from the split_domain_port function. Here's how that function looks

def split_domain_port(host):
    """
    Return a (domain, port) tuple from a given host.
    Returned domain is lowercased. If the host is invalid, the domain will be
    empty.
    """
    host = host.lower()

    if not host_validation_re.match(host):
        return '', ''

    if host[-1] == ']':
        # It's an IPv6 address without a port.
        return host, ''
    bits = host.rsplit(':', 1)
    domain, port = bits if len(bits) == 2 else (bits[0], '')
    # Remove a trailing dot (if present) from the domain.
    domain = domain[:-1] if domain.endswith('.') else domain
    return domain, port

Looks like if our url failed to be matched by the host_validation_re regex, the returned domain will be blank and will produce the above error. Here's what the host_validation_re looks like

host_validation_re = _lazy_re_compile(r"^([a-z0-9.-]+|\[[a-f0-9]*:[a-f0-9\.:]+\])(:\d+)?$")

If you know a bit about regular expressions, it's no mystery that this regex will fail to match a site that looks something like blue-forest.ide.company.io.

Of course, host validation by django is a good feature and ensures that only allowed sites are accessing our views so we wouldn't want to turn off this feature. However, during development, you may want to temporarily turn this off to unblock yourself.

The solution is to override the host_validation_re to something that will always match any website, something like [a-zA-z0-9.:]*. A good place to do this override is in manage.py

Let's add the following lines

from django.utils.regex_helper import _lazy_re_compile
import django.http.request

django.http.request.host_validation_re = _lazy_re_compile(r"[a-zA-z0-9.:]*")

Our manage.py now looks like this:

import os
import sys
from django.utils.regex_helper import _lazy_re_compile
import django.http.request

django.http.request.host_validation_re = _lazy_re_compile(r"[a-zA-z0-9.:]*")

def main():
    """Run administrative tasks."""
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project01.settings')
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)


if __name__ == '__main__':
    main()

And Voila! You should be able to access your django application now

image.png