Deployment¶
Any WSGI server runs a flask-peewee app. With the tutorial’s app.py:
$ pip install gunicorn
$ gunicorn -b 127.0.0.1:8000 app:app
$ pip install waitress
$ waitress-serve --listen=127.0.0.1:8000 app:app
$ pip install uwsgi
$ uwsgi --http 127.0.0.1:8000 --master --die-on-term --module app:app
--die-on-term makes SIGTERM stop uwsgi instead of reloading it.
The REST api’s basic-auth backends read the Authorization header. If
API writes start returning 401 behind a new server or proxy, check that
the header survives the hop.
Behind a proxy¶
When nginx or another proxy fronts the app, trust its forwarded headers so redirects and generated urls come out right, including when the app is mounted under a path prefix:
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_prefix=1)
Sessions¶
Login state is stored in the flask session. Use a long random
SECRET_KEY, and on HTTPS set the cookie flags:
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Lax'
Auth.login_user() marks the session permanent, so
PERMANENT_SESSION_LIFETIME bounds how long a login lasts (flask
defaults it to 31 days). See also clear_session in
Authentication.
CSRF protection¶
The form templates, the login page included, carry a hidden
_csrf_token input wired for
flask-seasurf:
from flask_seasurf import SeaSurf
csrf = SeaSurf(app)
csrf.exempt_urls(('/api',))
With SeaSurf initialized, a form post without a valid token is rejected with a 403. The REST api authenticates by header rather than by session, so exempt its prefix as above, or every API write is rejected too.
Static files¶
The admin’s stylesheets and scripts are served by its blueprint, which
any WSGI server handles. At admin traffic levels that is fine. Alias
/admin/static/ from the web server if you want it off the app.