Two Domains, One Flask

How to support multiple domains using a single Flask application

Posted 2019-04-28 15:08:39 by Ronie Martinez

I recently bought a new domain from Namecheap and I wanted to deploy a website using Flask on AWS Elastic Beanstalk. However, I do not want to create another Elastic Beanstalk application since there's not much content in my new website and I do not want to pay for additional cost. 

Enter Flask Host Matching

Flask host matching provides a way to perform matching on domain names. For example, if you have several domain names and you want to host these domains on a single Flask application, the host matching feature will come in handy.

Installing Flask

The first step is making sure that Flask is already installed on your platform. To install Flask, run any of the following command:

pip install flask  # using pip
easy_install flask  # using easy_install

Working Locally

For local development, updating the hosts file is a required step to use multiple domains. By default, localhost or 127.0.0.1 is used and we do not want to use these. For example, if we want to use the domains www.first.local and www.second.local, we need to add these lines to C:\Windows\System32\drivers\etc\hosts.

127.0.0.1       www.first.local
127.0.0.1       www.second.local

Flask @route() Decorator

The easiest way to perform routing in Flask is the use the @route() decorator.  The example below creates two index routes, one for www.first.local and one for www.second.local. To enable host matching, we pass the argument host_matching=True and static_host=FIRST_DOMAIN to the Flask() constructor then pass the argument host=<desired_host> to the @route() decorator. The argument static_host is required when using host_matching=True.

from flask import Flask

FIRST_DOMAIN = 'www.first.local'
SECOND_DOMAIN = 'www.second.local'

application = Flask(__name__, host_matching=True, static_host=FIRST_DOMAIN)


@application.route('/', host=FIRST_DOMAIN)
def first_index():
    return f"Hello from {FIRST_DOMAIN}!"


@application.route('/', host=SECOND_DOMAIN)
def second_index():
    return f"Hello from {SECOND_DOMAIN}!"


if __name__ == '__main__':
    r"""
    Add these to hosts (C:\Windows\System32\drivers\etc\hosts) file:
    127.0.0.1       www.first.local
    127.0.0.1       www.second.local
    """
    application.run(host=FIRST_DOMAIN, port=80, debug=True, threaded=True)

Flask Blueprint @route() Decorator

Blueprint @route() decorator does not support the parameter host. Instead, we use the partial() function. With the use of the partial function, we can simplify the usage similar to the first example.

from functools import partial

from flask import Flask, Blueprint

FIRST_DOMAIN = 'www.first.local'
SECOND_DOMAIN = 'www.second.local'

application = Flask(__name__, host_matching=True, static_host=FIRST_DOMAIN)
second = Blueprint('second', __name__)
second_route = partial(second.route, host=SECOND_DOMAIN)


@application.route('/', host=FIRST_DOMAIN)
def first_index():
    return f"Hello from {FIRST_DOMAIN}!"


@second_route('/')
def second_index():
    return f"Hello from {SECOND_DOMAIN}!"


application.register_blueprint(second)


if __name__ == '__main__':
    r"""
    Add these to hosts (C:\Windows\System32\drivers\etc\hosts) file:
    127.0.0.1       www.first.local
    127.0.0.1       www.second.local
    """
    application.run(host=FIRST_DOMAIN, port=80, debug=True, threaded=True)

Using Pluggable Views

To perform host matching on pluggable views, we only need to pass the host parameter to add_url_rule() function. This also applies to blueprints. The example below uses MethodView.

from flask import Flask
from flask.views import MethodView

FIRST_DOMAIN = 'www.first.local'
SECOND_DOMAIN = 'www.second.local'

application = Flask(__name__, host_matching=True, static_host=FIRST_DOMAIN)


class FirstIndexView(MethodView):

    def get(self):
        return f"Hello from {FIRST_DOMAIN}!"


class SecondIndexView(MethodView):

    def get(self):
        return f"Hello from {SECOND_DOMAIN}!"


application.add_url_rule('/', view_func=FirstIndexView.as_view('first_index'), host=FIRST_DOMAIN)
application.add_url_rule('/', view_func=SecondIndexView.as_view('second_index'), host=SECOND_DOMAIN)


if __name__ == '__main__':
    r"""
    Add these to hosts (C:\Windows\System32\drivers\etc\hosts) file:
    127.0.0.1       www.first.local
    127.0.0.1       www.second.local
    """
    application.run(host=FIRST_DOMAIN, port=80, debug=True, threaded=True)

Deployment on AWS Elastic Beanstalk

Deploying our application on AWS Elastic Beanstalk is straightforward. Just follow this guide from my previous blog, How I Created EasyAsPy from Scratch Using Flask. The only difference is  that it is needed to create another hosted zone in Route 53 and should mix the routes in a single certificate in Certificate Manager.

Can I use more than 2 domain names? Definitely! The purpose of the title is to make it click-baity ?! (If you are aware of a once trending item on the internet with similar sounding title)

The example codes can be downloaded from Github.

python flask elastic beanstalk namecheap certificate manager aws route 53


Share