I have two web domains, call them alpha.com and beta.com. I want to run two different public facing websites on these two different domains, but from a shared server and within the same flask app and application context. These websites will both share the backend app and database,
My current project structure is below:
app/
-static/
--alphacom_static/
--betacom_static/
--shared_backend_static/
-blueprints/
--alphacom_frontend_bluprint/
--betacom_frontend_blueprint/
--shared_backend_blueprint/
I'm serving beta.com running on localhost with flask/gevent through a reverse proxy on the server. I plan to just add a blueprint for alpha.com.
The route of the beta.com landing page is @blueprint.route(r'/', methods=['GET', 'POST'])
. When a user logs in at beta.com/login then they get directed to beta.com/app.
What is the approach using blueprints and routes, to bring alpha.com as a blueprint and when user logs in they are served alpha.com/app?
How can I modify the routes for alpha.com to avoid a collision with beta.com?
Improve performance in both Blocking and Non-Blocking web servers. Multitasking is the ability to execute multiple tasks or processes (almost) at the same time. Modern web servers like Flask, Django, and Tornado are all able to handle multiple requests simultaneously.
Enter application dispatching. With this you can combine multiple Flask applications at the WSGI level. This also allows you to combine any WSGI application. So if you have separate Flask, Django, or Dash applications you can run them in the same interpreter side by side if you want.
Flask applications are deployed on a web server, either the built-in server for development and testing or a suitable server (gunicorn, nginx etc.) for production. By default, the server can handle multiple client requests without the programmer having to do anything special in the application.
Flask will process one request per thread at the same time. If you have 2 processes with 4 threads each, that's 8 concurrent requests. Flask doesn't spawn or manage threads or processes.
I found it is not very well supported in flask's current stable release of Flask==0.12.2
. It theoretically can be done to a degree with host_matching. But in my testing, the static routes were always broken.
However, the flask development version on master at the time of writing has merged a pull request that makes it a little easier. Doing a pip install git+git://github.com/pallets/flask.git
will install Flask==0.13.dev0
. Then, using the factory pattern to create the flask app, you can set host_matching=True
and static_host=127.0.0.1:8000
in my case.
For me, my factory function looks like this:
def create_app(config_obj=None):
"""An application factory, as explained here:
http://flask.pocoo.org/docs/patterns/appfactories/.
:param config_object: The configuration object to use.
"""
app = Flask(__name__, host_matching=True, static_host='127.0.0.1:8000')
app.config.from_object(config_obj)
register_extensions(app)
register_blueprints(app)
return app
Another thing needed to make this work is to modify your hosts and set the domains you'd like to reference in the host file. On windows, this is found at C:\Windows\System32\drivers\etc\hosts
. At the bottom of the hosts file, I've modified as such:
# localhost name resolution is handled within DNS itself.
# 127.0.0.1 localhost
# ::1 localhost
127.0.0.1 alpha.local
127.0.0.1 beta.local
127.0.0.1 local
You need to run this solution behind a reverse proxy (NGINX on Linux or IIS on Windows) and set it up to forward the appropriate requests to alpha.local:8000
or beta.local:8000
in the case of this example. But, you will modify <subdomain>.local:<port>
based on your actual need.
Another issue you will deal with is browser complaining about CORS requests, so you may need to set headers for Access-Control-Allow-Origin: *
or specific domains like Access-Control-Allow-Origin: http://beta.local:8000
. For development server, I found this helpful for CORS to allow font access:
@blueprint.after_app_request
def add_headers_to_fontawesome_static_files(response):
"""
Fix for font-awesome files: after Flask static send_file() does its
thing, but before the response is sent, add an
Access-Control-Allow-Origin: *
HTTP header to the response (otherwise browsers complain).
"""
if not os.environ.get('APP_ENV') == 'prod':
if request.path and re.search(r'\.(ttf|woff|woff2|svg|eot)$', request.path):
response.headers.add('Access-Control-Allow-Origin', '*')
return response
Note that for production, you must set your modified headers on your proxy (like NGINX or IIS) and the above function is not useful for produciton.
Last, with host_matching=True
then the routes must be specified for hosts, example being below:
@blueprint.route('/about/', methods=['GET', 'POST'],
host='<string:subdom>.local:8000')
def about_app(**kwargs):
"""The about page."""
return render_template('about.html')
If you do the routes as above, it is helpful to set the url_defualts in a function somewhere in your app, like below:
@blueprint.app_url_defaults
def add_subdom(endpoint, values):
path = request.url_root
if 'alpha.local' in path:
g.subdom = 'alpha'
elif 'beta.local' in path:
g.subdom = 'beta'
if current_app.url_map.is_endpoint_expecting(endpoint, 'subdom'):
values['subdom'] = g.subdom
Good luck, this was not easy.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With