Configuration Handling¶
Applications need some kind of configuration. There are different settingsyou might want to change depending on the application environment liketoggling the debug mode, setting the secret key, and other suchenvironment-specific things.
The way Flask is designed usually requires the configuration to beavailable when the application starts up. You can hard code theconfiguration in the code, which for many small applications is notactually that bad, but there are better ways.
Independent of how you load your config, there is a config objectavailable which holds the loaded configuration values:Theconfig attribute of theFlaskobject. This is the place where Flask itself puts certain configurationvalues and also where extensions can put their configuration values. Butthis is also where you can have your own configuration.
Configuration Basics¶
Theconfig is actually a subclass of a dictionary andcan be modified just like any dictionary:
app=Flask(__name__)app.config['TESTING']=True
Certain configuration values are also forwarded to theFlask object so you can read and write them from there:
app.testing=True
To update multiple keys at once you can use thedict.update()method:
app.config.update(TESTING=True,SECRET_KEY='192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf')
Debug Mode¶
TheDEBUG config value is special because it may behave inconsistently ifchanged after the app has begun setting up. In order to set debug mode reliably, use the--debug option on theflask orflaskrun command.flaskrun will use theinteractive debugger and reloader by default in debug mode.
$ flask --app hello run --debug
Using the option is recommended. While it is possible to setDEBUG in yourconfig or code, this is strongly discouraged. It can’t be read early by theflaskrun command, and some systems or extensions may have already configuredthemselves based on a previous value.
Builtin Configuration Values¶
The following configuration values are used internally by Flask:
- DEBUG¶
Whether debug mode is enabled. When using
flaskrunto start the developmentserver, an interactive debugger will be shown for unhandled exceptions, and theserver will be reloaded when code changes. Thedebugattributemaps to this config key. This is set with theFLASK_DEBUGenvironment variable.It may not behave as expected if set in code.Do not enable debug mode when deploying in production.
Default:
False
- TESTING¶
Enable testing mode. Exceptions are propagated rather than handled by thethe app’s error handlers. Extensions may also change their behavior tofacilitate easier testing. You should enable this in your own tests.
Default:
False
- PROPAGATE_EXCEPTIONS¶
Exceptions are re-raised rather than being handled by the app’s errorhandlers. If not set, this is implicitly true if
TESTINGorDEBUGis enabled.Default:
None
- TRAP_HTTP_EXCEPTIONS¶
If there is no handler for an
HTTPException-type exception, re-raise itto be handled by the interactive debugger instead of returning it as asimple error response.Default:
False
- TRAP_BAD_REQUEST_ERRORS¶
Trying to access a key that doesn’t exist from request dicts like
argsandformwill return a 400 Bad Request error page. Enable this to treatthe error as an unhandled exception instead so that you get the interactivedebugger. This is a more specific version ofTRAP_HTTP_EXCEPTIONS. Ifunset, it is enabled in debug mode.Default:
None
- SECRET_KEY¶
A secret key that will be used for securely signing the session cookieand can be used for any other security related needs by extensions or yourapplication. It should be a long random
bytesorstr. Forexample, copy the output of this to your config:$ python -c 'import secrets; print(secrets.token_hex())''192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'
Do not reveal the secret key when posting questions or committing code.
Default:
None
- SECRET_KEY_FALLBACKS¶
A list of old secret keys that can still be used for unsigning. This allowsa project to implement key rotation without invalidating active sessions orother recently-signed secrets.
Keys should be removed after an appropriate period of time, as checking eachadditional key adds some overhead.
Order should not matter, but the default implementation will test the lastkey in the list first, so it might make sense to order oldest to newest.
Flask’s built-in secure cookie session supports this. Extensions that use
SECRET_KEYmay not support this yet.Default:
NoneAdded in version 3.1.
- SESSION_COOKIE_NAME¶
The name of the session cookie. Can be changed in case you already have acookie with the same name.
Default:
'session'
- SESSION_COOKIE_DOMAIN¶
The value of the
Domainparameter on the session cookie. If not set, browserswill only send the cookie to the exact domain it was set from. Otherwise, theywill send it to any subdomain of the given value as well.Not setting this value is more restricted and secure than setting it.
Default:
NoneWarning
If this is changed after the browser created a cookie is created withone setting, it may result in another being created. Browsers may sendsend both in an undefined order. In that case, you may want to change
SESSION_COOKIE_NAMEas well or otherwise invalidate old sessions.Changelog
Changed in version 2.3:Not set by default, does not fall back to
SERVER_NAME.
- SESSION_COOKIE_PATH¶
The path that the session cookie will be valid for. If not set, the cookiewill be valid underneath
APPLICATION_ROOTor/if that is not set.Default:
None
- SESSION_COOKIE_HTTPONLY¶
Browsers will not allow JavaScript access to cookies marked as “HTTP only”for security.
Default:
True
- SESSION_COOKIE_SECURE¶
Browsers will only send cookies with requests over HTTPS if the cookie ismarked “secure”. The application must be served over HTTPS for this to makesense.
Default:
False
- SESSION_COOKIE_PARTITIONED¶
Browsers will send cookies based on the top-level document’s domain, ratherthan only the domain of the document setting the cookie. This prevents thirdparty cookies set in iframes from “leaking” between separate sites.
Browsers are beginning to disallow non-partitioned third party cookies, soyou need to mark your cookies partitioned if you expect them to work in suchembedded situations.
Enabling this implicitly enables
SESSION_COOKIE_SECUREas well, asit is only valid when served over HTTPS.Default:
FalseAdded in version 3.1.
- SESSION_COOKIE_SAMESITE¶
Restrict how cookies are sent with requests from external sites. Canbe set to
'Lax'(recommended) or'Strict'.SeeSet-Cookie options.Default:
NoneChangelog
Added in version 1.0.
- PERMANENT_SESSION_LIFETIME¶
If
session.permanentis true, the cookie’s expiration will be set thisnumber of seconds in the future. Can either be adatetime.timedeltaor anint.Flask’s default cookie implementation validates that the cryptographicsignature is not older than this value.
Default:
timedelta(days=31)(2678400seconds)
- SESSION_REFRESH_EACH_REQUEST¶
Control whether the cookie is sent with every response when
session.permanentis true. Sending the cookie every time (the default)can more reliably keep the session from expiring, but uses more bandwidth.Non-permanent sessions are not affected.Default:
True
- USE_X_SENDFILE¶
When serving files, set the
X-Sendfileheader instead of serving thedata with Flask. Some web servers, such as Apache, recognize this and servethe data more efficiently. This only makes sense when using such a server.Default:
False
- SEND_FILE_MAX_AGE_DEFAULT¶
When serving files, set the cache control max age to this number ofseconds. Can be a
datetime.timedeltaor anint.Override this value on a per-file basis usingget_send_file_max_age()on the application orblueprint.If
None,send_filetells the browser to use conditionalrequests will be used instead of a timed cache, which is usuallypreferable.Default:
None
- TRUSTED_HOSTS¶
Validate
Request.hostand other attributes that use it againstthese trusted values. Raise aSecurityErrorifthe host is invalid, which results in a 400 error. If it isNone, allhosts are valid. Each value is either an exact match, or can start witha dot.to match any subdomain.Validation is done during routing against this value.
before_requestandafter_requestcallbacks will still be called.Default:
NoneAdded in version 3.1.
- SERVER_NAME¶
Inform the application what host and port it is bound to.
Must be set if
subdomain_matchingis enabled, to be able to extract thesubdomain from the request.Must be set for
url_forto generate external URLs outside of arequest context.Default:
NoneChanged in version 3.1:Does not restrict requests to only this domain, for both
subdomain_matchingandhost_matching.Changelog
Changed in version 2.3:Does not affect
SESSION_COOKIE_DOMAIN.Changed in version 1.0:Does not implicitly enable
subdomain_matching.
- APPLICATION_ROOT¶
Inform the application what path it is mounted under by the application /web server. This is used for generating URLs outside the context of arequest (inside a request, the dispatcher is responsible for setting
SCRIPT_NAMEinstead; seeApplication Dispatchingfor examples of dispatch configuration).Will be used for the session cookie path if
SESSION_COOKIE_PATHis notset.Default:
'/'
- PREFERRED_URL_SCHEME¶
Use this scheme for generating external URLs when not in a request context.
Default:
'http'
- MAX_CONTENT_LENGTH¶
The maximum number of bytes that will be read during this request. Ifthis limit is exceeded, a 413
RequestEntityTooLargeerror is raised. If it is set toNone, no limit is enforced at theFlask application level. However, if it isNoneand the request has noContent-Lengthheader and the WSGI server does not indicate that itterminates the stream, then no data is read to avoid an infinite stream.Each request defaults to this config. It can be set on a specific
Request.max_content_lengthto apply the limit to that specificview. This should be set appropriately based on an application’s or view’sspecific needs.Default:
NoneChangelog
Added in version 0.6.
- MAX_FORM_MEMORY_SIZE¶
The maximum size in bytes any non-file form field may be in a
multipart/form-databody. If this limit is exceeded, a 413RequestEntityTooLargeerror is raised. If it isset toNone, no limit is enforced at the Flask application level.Each request defaults to this config. It can be set on a specific
Request.max_form_memory_partsto apply the limit to that specificview. This should be set appropriately based on an application’s or view’sspecific needs.Default:
500_000Added in version 3.1.
- MAX_FORM_PARTS¶
The maximum number of fields that may be present in a
multipart/form-databody. If this limit is exceeded, a 413RequestEntityTooLargeerror is raised. If itis set toNone, no limit is enforced at the Flask application level.Each request defaults to this config. It can be set on a specific
Request.max_form_partsto apply the limit to that specific view.This should be set appropriately based on an application’s or view’sspecific needs.Default:
1_000Added in version 3.1.
- TEMPLATES_AUTO_RELOAD¶
Reload templates when they are changed. If not set, it will be enabled indebug mode.
Default:
None
- EXPLAIN_TEMPLATE_LOADING¶
Log debugging information tracing how a template file was loaded. This canbe useful to figure out why a template was not loaded or the wrong fileappears to be loaded.
Default:
False
- MAX_COOKIE_SIZE¶
Warn if cookie headers are larger than this many bytes. Defaults to
4093. Larger cookies may be silently ignored by browsers. Set to0to disable the warning.
- PROVIDE_AUTOMATIC_OPTIONS¶
Set to
Falseto disable the automatic addition of OPTIONSresponses. This can be overridden per route by altering theprovide_automatic_optionsattribute.
Added in version 3.10:AddedPROVIDE_AUTOMATIC_OPTIONS to control the defaultaddition of autogenerated OPTIONS responses.
Changelog
Changed in version 2.3:JSON_AS_ASCII,JSON_SORT_KEYS,JSONIFY_MIMETYPE, andJSONIFY_PRETTYPRINT_REGULAR were removed. The defaultapp.json provider hasequivalent attributes instead.
Changed in version 2.3:ENV was removed.
Changed in version 2.2:RemovedPRESERVE_CONTEXT_ON_EXCEPTION.
Changed in version 1.0:LOGGER_NAME andLOGGER_HANDLER_POLICY were removed. SeeLogging for information about configuration.
AddedENV to reflect theFLASK_ENV environmentvariable.
AddedSESSION_COOKIE_SAMESITE to control the sessioncookie’sSameSite option.
AddedMAX_COOKIE_SIZE to control a warning from Werkzeug.
Added in version 0.11:SESSION_REFRESH_EACH_REQUEST,TEMPLATES_AUTO_RELOAD,LOGGER_HANDLER_POLICY,EXPLAIN_TEMPLATE_LOADING
Added in version 0.10:JSON_AS_ASCII,JSON_SORT_KEYS,JSONIFY_PRETTYPRINT_REGULAR
Added in version 0.9:PREFERRED_URL_SCHEME
Added in version 0.8:TRAP_BAD_REQUEST_ERRORS,TRAP_HTTP_EXCEPTIONS,APPLICATION_ROOT,SESSION_COOKIE_DOMAIN,SESSION_COOKIE_PATH,SESSION_COOKIE_HTTPONLY,SESSION_COOKIE_SECURE
Added in version 0.7:PROPAGATE_EXCEPTIONS,PRESERVE_CONTEXT_ON_EXCEPTION
Added in version 0.6:MAX_CONTENT_LENGTH
Added in version 0.5:SERVER_NAME
Added in version 0.4:LOGGER_NAME
Configuring from Python Files¶
Configuration becomes more useful if you can store it in a separate file, ideallylocated outside the actual application package. You can deploy your application, thenseparately configure it for the specific deployment.
A common pattern is this:
app=Flask(__name__)app.config.from_object('yourapplication.default_settings')app.config.from_envvar('YOURAPPLICATION_SETTINGS')
This first loads the configuration from theyourapplication.default_settings module and then overrides the valueswith the contents of the file theYOURAPPLICATION_SETTINGSenvironment variable points to. This environment variable can be setin the shell before starting the server:
$ export YOURAPPLICATION_SETTINGS=/path/to/settings.cfg$ flask run * Running on http://127.0.0.1:5000/
$ set -x YOURAPPLICATION_SETTINGS /path/to/settings.cfg$ flask run * Running on http://127.0.0.1:5000/
> set YOURAPPLICATION_SETTINGS=\path\to\settings.cfg> flask run * Running on http://127.0.0.1:5000/
> $env:YOURAPPLICATION_SETTINGS = "\path\to\settings.cfg"> flask run * Running on http://127.0.0.1:5000/
The configuration files themselves are actual Python files. Only valuesin uppercase are actually stored in the config object later on. So makesure to use uppercase letters for your config keys.
Here is an example of a configuration file:
# Example configurationSECRET_KEY='192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'
Make sure to load the configuration very early on, so that extensions havethe ability to access the configuration when starting up. There are othermethods on the config object as well to load from individual files. For acomplete reference, read theConfig object’sdocumentation.
Configuring from Data Files¶
It is also possible to load configuration from a file in a format ofyour choice usingfrom_file(). For example to loadfrom a TOML file:
importtomllibapp.config.from_file("config.toml",load=tomllib.load,text=False)
Or from a JSON file:
importjsonapp.config.from_file("config.json",load=json.load)
Configuring from Environment Variables¶
In addition to pointing to configuration files using environmentvariables, you may find it useful (or necessary) to control yourconfiguration values directly from the environment. Flask can beinstructed to load all environment variables starting with a specificprefix into the config usingfrom_prefixed_env().
Environment variables can be set in the shell before starting theserver:
$ export FLASK_SECRET_KEY="5f352379324c22463451387a0aec5d2f"$ export FLASK_MAIL_ENABLED=false$ flask run * Running on http://127.0.0.1:5000/
$ set -x FLASK_SECRET_KEY "5f352379324c22463451387a0aec5d2f"$ set -x FLASK_MAIL_ENABLED false$ flask run * Running on http://127.0.0.1:5000/
> set FLASK_SECRET_KEY="5f352379324c22463451387a0aec5d2f"> set FLASK_MAIL_ENABLED=false> flask run * Running on http://127.0.0.1:5000/
> $env:FLASK_SECRET_KEY = "5f352379324c22463451387a0aec5d2f"> $env:FLASK_MAIL_ENABLED = "false"> flask run * Running on http://127.0.0.1:5000/
The variables can then be loaded and accessed via the config with a keyequal to the environment variable name without the prefix i.e.
app.config.from_prefixed_env()app.config["SECRET_KEY"]# Is "5f352379324c22463451387a0aec5d2f"
The prefix isFLASK_ by default. This is configurable via theprefix argument offrom_prefixed_env().
Values will be parsed to attempt to convert them to a more specific typethan strings. By defaultjson.loads() is used, so any valid JSONvalue is possible, including lists and dicts. This is configurable viatheloads argument offrom_prefixed_env().
When adding a boolean value with the default JSON parsing, only “true”and “false”, lowercase, are valid values. Keep in mind that anynon-empty string is consideredTrue by Python.
It is possible to set keys in nested dictionaries by separating thekeys with double underscore (__). Any intermediate keys that don’texist on the parent dict will be initialized to an empty dict.
$ export FLASK_MYAPI__credentials__username=user123
app.config["MYAPI"]["credentials"]["username"]# Is "user123"
On Windows, environment variable keys are always uppercase, thereforethe above example would end up asMYAPI__CREDENTIALS__USERNAME.
For even more config loading features, including merging andcase-insensitive Windows support, try a dedicated library such asDynaconf, which includes integration with Flask.
Configuration Best Practices¶
The downside with the approach mentioned earlier is that it makes testinga little harder. There is no single 100% solution for this problem ingeneral, but there are a couple of things you can keep in mind to improvethat experience:
Create your application in a function and register blueprints on it.That way you can create multiple instances of your application withdifferent configurations attached which makes unit testing a loteasier. You can use this to pass in configuration as needed.
Do not write code that needs the configuration at import time. If youlimit yourself to request-only accesses to the configuration you canreconfigure the object later on as needed.
Make sure to load the configuration very early on, so thatextensions can access the configuration when calling
init_app.
Development / Production¶
Most applications need more than one configuration. There should be atleast separate configurations for the production server and the one usedduring development. The easiest way to handle this is to use a defaultconfiguration that is always loaded and part of the version control, and aseparate configuration that overrides the values as necessary as mentionedin the example above:
app=Flask(__name__)app.config.from_object('yourapplication.default_settings')app.config.from_envvar('YOURAPPLICATION_SETTINGS')
Then you just have to add a separateconfig.py file and exportYOURAPPLICATION_SETTINGS=/path/to/config.py and you are done. Howeverthere are alternative ways as well. For example you could use imports orsubclassing.
What is very popular in the Django world is to make the import explicit inthe config file by addingfromyourapplication.default_settingsimport* to the top of the file and then overriding the changes by hand.You could also inspect an environment variable likeYOURAPPLICATION_MODE and set that toproduction,development etcand import different hard-coded files based on that.
An interesting pattern is also to use classes and inheritance forconfiguration:
classConfig(object):TESTING=FalseclassProductionConfig(Config):DATABASE_URI='mysql://user@localhost/foo'classDevelopmentConfig(Config):DATABASE_URI="sqlite:////tmp/foo.db"classTestingConfig(Config):DATABASE_URI='sqlite:///:memory:'TESTING=True
To enable such a config you just have to call intofrom_object():
app.config.from_object('configmodule.ProductionConfig')
Note thatfrom_object() does not instantiate the classobject. If you need to instantiate the class, such as to access a property,then you must do so before callingfrom_object():
fromconfigmoduleimportProductionConfigapp.config.from_object(ProductionConfig())# Alternatively, import via string:fromwerkzeug.utilsimportimport_stringcfg=import_string('configmodule.ProductionConfig')()app.config.from_object(cfg)
Instantiating the configuration object allows you to use@property inyour configuration classes:
classConfig(object):"""Base config, uses staging database server."""TESTING=FalseDB_SERVER='192.168.1.56'@propertydefDATABASE_URI(self):# Note: all capsreturnf"mysql://user@{self.DB_SERVER}/foo"classProductionConfig(Config):"""Uses production database server."""DB_SERVER='192.168.19.32'classDevelopmentConfig(Config):DB_SERVER='localhost'classTestingConfig(Config):DB_SERVER='localhost'DATABASE_URI='sqlite:///:memory:'
There are many different ways and it’s up to you how you want to manageyour configuration files. However here a list of good recommendations:
Keep a default configuration in version control. Either populate theconfig with this default configuration or import it in your ownconfiguration files before overriding values.
Use an environment variable to switch between the configurations.This can be done from outside the Python interpreter and makesdevelopment and deployment much easier because you can quickly andeasily switch between different configs without having to touch thecode at all. If you are working often on different projects you caneven create your own script for sourcing that activates a virtualenvand exports the development configuration for you.
Use a tool likefabric to push code and configuration separatelyto the production server(s).
Instance Folders¶
Changelog
Added in version 0.8.
Flask 0.8 introduces instance folders. Flask for a long time made itpossible to refer to paths relative to the application’s folder directly(viaFlask.root_path). This was also how many developers loadedconfigurations stored next to the application. Unfortunately however thisonly works well if applications are not packages in which case the rootpath refers to the contents of the package.
With Flask 0.8 a new attribute was introduced:Flask.instance_path. It refers to a new concept called the“instance folder”. The instance folder is designed to not be underversion control and be deployment specific. It’s the perfect place todrop things that either change at runtime or configuration files.
You can either explicitly provide the path of the instance folder whencreating the Flask application or you can let Flask autodetect theinstance folder. For explicit configuration use theinstance_pathparameter:
app=Flask(__name__,instance_path='/path/to/instance/folder')
Please keep in mind that this pathmust be absolute when provided.
If theinstance_path parameter is not provided the following defaultlocations are used:
Uninstalled module:
/myapp.py/instance
Uninstalled package:
/myapp/__init__.py/instance
Installed module or package:
$PREFIX/lib/pythonX.Y/site-packages/myapp$PREFIX/var/myapp-instance
$PREFIXis the prefix of your Python installation. This can be/usror the path to your virtualenv. You can print the value ofsys.prefixto see what the prefix is set to.
Since the config object provided loading of configuration files fromrelative filenames we made it possible to change the loading via filenamesto be relative to the instance path if wanted. The behavior of relativepaths in config files can be flipped between “relative to the applicationroot” (the default) to “relative to instance folder” via theinstance_relative_config switch to the application constructor:
app=Flask(__name__,instance_relative_config=True)
Here is a full example of how to configure Flask to preload the configfrom a module and then override the config from a file in the instancefolder if it exists:
app=Flask(__name__,instance_relative_config=True)app.config.from_object('yourapplication.default_settings')app.config.from_pyfile('application.cfg',silent=True)
The path to the instance folder can be found via theFlask.instance_path. Flask also provides a shortcut to open afile from the instance folder withFlask.open_instance_resource().
Example usage for both:
filename=os.path.join(app.instance_path,'application.cfg')withopen(filename)asf:config=f.read()# or via open_instance_resource:withapp.open_instance_resource('application.cfg')asf:config=f.read()