I have a Flask project/app and want it to be served mainly from app.example.com
. I also have a single blueprint inside this app which should only be served from api.example.com
.
Now, if I set app
as the default subdomain, I'm unable to override this default in other blueprints which should be served from a different subdomain (e.g. api
). In fact, any blueprints created with a different subdomain will 404.
In other words, the code below doesn't work (api.example.com/test2
will 404):
# -*- coding: utf-8 -*-
from flask import Flask, Blueprint
app = Flask(__name__)
app.config['SERVER_NAME'] = 'example.com'
app.url_map.default_subdomain = 'app' # set default subdomain, intending to override it below for `api.*`
appbp = Blueprint('app', 'app')
apibp = Blueprint('api', 'api')
@appbp.route('/test1')
def app_hello():
# this works (app.example.com/test1)
return 'appbp.app_hello'
@apibp.route('/test2')
def api_hello():
# this will 404 (api.example.com/test2)
return 'apibp.api_hello'
app.register_blueprint(appbp) # this works, serves from `app.example.com`
app.register_blueprint(apibp, subdomain='api') # doesn't work, `api.example.com/test2` will 404, so will `app.example.com/test2` (tried just in case it was using the default subdomain instead)
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8888, debug=True)
However, if I don't set a default subdomain and instead set a subdomain each time I register a blueprint, it magically works for both app
and api
:
# -*- coding: utf-8 -*-
from flask import Flask, Blueprint
app = Flask(__name__)
app.config['SERVER_NAME'] = 'example.com'
# app.url_map.default_subdomain = 'app' # now try without a default
appbp = Blueprint('app', 'app')
apibp = Blueprint('api', 'api')
@appbp.route('/test1')
def app_hello():
# this works (app.example.com/test1)
return 'appbp.app_hello'
@apibp.route('/test2')
def api_hello():
# this works (api.example.com/test2)
return 'apibp.api_hello'
app.register_blueprint(appbp, subdomain='app') # works, explicitly set subdomain on each blueprint
app.register_blueprint(apibp, subdomain='api') # works, explicitly set subdomain on each blueprint
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8888, debug=True)
In both examples, it appears the blueprints are registered with the correct subdomain:
<Rule 'app|/test1' (OPTIONS, GET, HEAD) -> app.app_hello>
<Rule 'api|/test2' (OPTIONS, GET, HEAD) -> api.api_hello>
But, clearly, there's a difference between setting app.url_map.default_subdomain
intending to override it later and just explicitly setting subdomains manually.
Any idea what's going on here?
Bonus points: which of these is the preferred way to set a subdomain? I've seen it done both ways.
app.register_blueprint(apibp, subdomain='api')
vs.
apibp = Blueprint('api', 'api', subdomain='api')
app.register_blueprint(apibp)
What's missing is the subdomain_matching
option for Flask()
:
subdomain_matching – consider the subdomain relative to
SERVER_NAME
when matching routes. Defaults to False.
Since app
is a relative name, you need to set this to True
:
app = Flask(__name__, subdomain_matching=True)
This used to be done implicitly, but as of Flask 1.0 it's an explicit switch. The change was made because different people had different expectations of what setting SERVER_NAME
means, see Flask issue #998.
As for where you set the subdomain
option, that's your choice. If you are reusing blueprints in different contexts, then it makes more sense to set the subdomain option in the app.register_blueprint()
call, while setting it in the Blueprint()
instance call may make it more self-documenting if you are creating that blueprint object closer to your routes for and so want to make it clearer to someone working on that code that they are affecting a specific subdomain only.