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 usingflaskrun to start the developmentserver, an interactive debugger will be shown for unhandled exceptions, and theserver will be reloaded when code changes. Thedebug attributemaps to this config key. This is set with theFLASK_DEBUG environment 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 ifTESTING orDEBUGis enabled.

Default:None

TRAP_HTTP_EXCEPTIONS

If there is no handler for anHTTPException-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 likeargsandform will 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 randombytes orstr. 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 useSECRET_KEY may not support this yet.

Default:None

Added 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 theDomain parameter 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:None

Warning

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 changeSESSION_COOKIE_NAME as well or otherwise invalidate old sessions.

Changelog

Changed in version 2.3:Not set by default, does not fall back toSERVER_NAME.

SESSION_COOKIE_PATH

The path that the session cookie will be valid for. If not set, the cookiewill be valid underneathAPPLICATION_ROOT or/ 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 enablesSESSION_COOKIE_SECURE as well, asit is only valid when served over HTTPS.

Default:False

Added 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:None

Changelog

Added in version 1.0.

PERMANENT_SESSION_LIFETIME

Ifsession.permanent is true, the cookie’s expiration will be set thisnumber of seconds in the future. Can either be adatetime.timedelta or anint.

Flask’s default cookie implementation validates that the cryptographicsignature is not older than this value.

Default:timedelta(days=31) (2678400 seconds)

SESSION_REFRESH_EACH_REQUEST

Control whether the cookie is sent with every response whensession.permanent is 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 theX-Sendfile header 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 adatetime.timedelta or anint.Override this value on a per-file basis usingget_send_file_max_age() on the application orblueprint.

IfNone,send_file tells the browser to use conditionalrequests will be used instead of a timed cache, which is usuallypreferable.

Default:None

TRUSTED_HOSTS

ValidateRequest.host and other attributes that use it againstthese trusted values. Raise aSecurityError ifthe 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_request andafter_request callbacks will still be called.

Default:None

Added in version 3.1.

SERVER_NAME

Inform the application what host and port it is bound to.

Must be set ifsubdomain_matching is enabled, to be able to extract thesubdomain from the request.

Must be set forurl_for to generate external URLs outside of arequest context.

Default:None

Changed in version 3.1:Does not restrict requests to only this domain, for bothsubdomain_matching andhost_matching.

Changelog

Changed in version 2.3:Does not affectSESSION_COOKIE_DOMAIN.

Changed in version 1.0:Does not implicitly enablesubdomain_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 settingSCRIPT_NAME instead; seeApplication Dispatchingfor examples of dispatch configuration).

Will be used for the session cookie path ifSESSION_COOKIE_PATH is 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 413RequestEntityTooLargeerror is raised. If it is set toNone, no limit is enforced at theFlask application level. However, if it isNone and the request has noContent-Length header 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 specificRequest.max_content_length to apply the limit to that specificview. This should be set appropriately based on an application’s or view’sspecific needs.

Default:None

Changelog

Added in version 0.6.

MAX_FORM_MEMORY_SIZE

The maximum size in bytes any non-file form field may be in amultipart/form-data body. If this limit is exceeded, a 413RequestEntityTooLarge error 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 specificRequest.max_form_memory_parts to apply the limit to that specificview. This should be set appropriately based on an application’s or view’sspecific needs.

Default:500_000

Added in version 3.1.

MAX_FORM_PARTS

The maximum number of fields that may be present in amultipart/form-data body. If this limit is exceeded, a 413RequestEntityTooLarge error 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 specificRequest.max_form_parts to apply the limit to that specific view.This should be set appropriately based on an application’s or view’sspecific needs.

Default:1_000

Added 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 to4093. Larger cookies may be silently ignored by browsers. Set to0 to disable the warning.

PROVIDE_AUTOMATIC_OPTIONS

Set toFalse to disable the automatic addition of OPTIONSresponses. This can be overridden per route by altering theprovide_automatic_options attribute.

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/

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/

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:

  1. 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.

  2. 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.

  3. Make sure to load the configuration very early on, so thatextensions can access the configuration when callinginit_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

    $PREFIX is the prefix of your Python installation. This can be/usr or the path to your virtualenv. You can print the value ofsys.prefix to 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()