Merge pull request #4637 from pallets/docs-deploy

rewrite deployment docs
This commit is contained in:
David Lord 2022-06-12 14:03:55 -07:00 committed by GitHub
commit b0a144d0c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 807 additions and 829 deletions

View file

@ -70,8 +70,8 @@ If you wish to use background tasks it is best to use a task queue to
trigger background work, rather than spawn tasks in a view
function. With that in mind you can spawn asyncio tasks by serving
Flask with an ASGI server and utilising the asgiref WsgiToAsgi adapter
as described in :ref:`asgi`. This works as the adapter creates an
event loop that runs continually.
as described in :doc:`deploying/asgi`. This works as the adapter creates
an event loop that runs continually.
When to use Quart instead

View file

@ -0,0 +1,66 @@
Apache httpd
============
`Apache httpd`_ is a fast, production level HTTP server. When serving
your application with one of the WSGI servers listed in :doc:`index`, it
is often good or necessary to put a dedicated HTTP server in front of
it. This "reverse proxy" can handle incoming requests, TLS, and other
security and performance concerns better than the WSGI server.
httpd can be installed using your system package manager, or a pre-built
executable for Windows. Installing and running httpd itself is outside
the scope of this doc. This page outlines the basics of configuring
httpd to proxy your application. Be sure to read its documentation to
understand what features are available.
.. _Apache httpd: https://httpd.apache.org/
Domain Name
-----------
Acquiring and configuring a domain name is outside the scope of this
doc. In general, you will buy a domain name from a registrar, pay for
server space with a hosting provider, and then point your registrar
at the hosting provider's name servers.
To simulate this, you can also edit your ``hosts`` file, located at
``/etc/hosts`` on Linux. Add a line that associates a name with the
local IP.
Modern Linux systems may be configured to treat any domain name that
ends with ``.localhost`` like this without adding it to the ``hosts``
file.
.. code-block:: python
:caption: ``/etc/hosts``
127.0.0.1 hello.localhost
Configuration
-------------
The httpd configuration is located at ``/etc/httpd/conf/httpd.conf`` on
Linux. It may be different depending on your operating system. Check the
docs and look for ``httpd.conf``.
Remove or comment out any existing ``DocumentRoot`` directive. Add the
config lines below. We'll assume the WSGI server is listening locally at
``http://127.0.0.1:8000``.
.. code-block:: apache
:caption: ``/etc/httpd/conf/httpd.conf``
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
ProxyPass / http://127.0.0.1:8000/
RequestHeader set X-Forwarded-Proto http
RequestHeader set X-Forwarded-Prefix /
The ``LoadModule`` lines might already exist. If so, make sure they are
uncommented instead of adding them manually.
Then :doc:`proxy_fix` so that your application uses the ``X-Forwarded``
headers. ``X-Forwarded-For`` and ``X-Forwarded-Host`` are automatically
set by ``ProxyPass``.

View file

@ -1,5 +1,3 @@
.. _asgi:
ASGI
====

View file

@ -1,61 +0,0 @@
CGI
===
If all other deployment methods do not work, CGI will work for sure.
CGI is supported by all major servers but usually has a sub-optimal
performance.
This is also the way you can use a Flask application on Google's `App
Engine`_, where execution happens in a CGI-like environment.
.. admonition:: Watch Out
Please make sure in advance that any ``app.run()`` calls you might
have in your application file are inside an ``if __name__ ==
'__main__':`` block or moved to a separate file. Just make sure it's
not called because this will always start a local WSGI server which
we do not want if we deploy that application to CGI / app engine.
With CGI, you will also have to make sure that your code does not contain
any ``print`` statements, or that ``sys.stdout`` is overridden by something
that doesn't write into the HTTP response.
Creating a `.cgi` file
----------------------
First you need to create the CGI application file. Let's call it
:file:`yourapplication.cgi`::
#!/usr/bin/python
from wsgiref.handlers import CGIHandler
from yourapplication import app
CGIHandler().run(app)
Server Setup
------------
Usually there are two ways to configure the server. Either just copy the
``.cgi`` into a :file:`cgi-bin` (and use `mod_rewrite` or something similar to
rewrite the URL) or let the server point to the file directly.
In Apache for example you can put something like this into the config:
.. sourcecode:: apache
ScriptAlias /app /path/to/the/application.cgi
On shared webhosting, though, you might not have access to your Apache config.
In this case, a file called ``.htaccess``, sitting in the public directory
you want your app to be available, works too but the ``ScriptAlias`` directive
won't work in that case:
.. sourcecode:: apache
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f # Don't interfere with static files
RewriteRule ^(.*)$ /path/to/the/application.cgi/$1 [L]
For more information consult the documentation of your webserver.
.. _App Engine: https://cloud.google.com/appengine/docs/

View file

@ -0,0 +1,80 @@
eventlet
========
Prefer using :doc:`gunicorn` with eventlet workers rather than using
`eventlet`_ directly. Gunicorn provides a much more configurable and
production-tested server.
`eventlet`_ allows writing asynchronous, coroutine-based code that looks
like standard synchronous Python. It uses `greenlet`_ to enable task
switching without writing ``async/await`` or using ``asyncio``.
:doc:`gevent` is another library that does the same thing. Certain
dependencies you have, or other considerations, may affect which of the
two you choose to use.
eventlet provides a WSGI server that can handle many connections at once
instead of one per worker process. You must actually use eventlet in
your own code to see any benefit to using the server.
.. _eventlet: https://eventlet.net/
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
Installing
----------
When using eventlet, greenlet>=1.0 is required, otherwise context locals
such as ``request`` will not work as expected. When using PyPy,
PyPy>=7.3.7 is required.
Create a virtualenv, install your application, then install
``eventlet``.
.. code-block:: text
$ cd hello-app
$ python -m venv venv
$ . venv/bin/activate
$ pip install . # install your application
$ pip install eventlet
Running
-------
To use eventlet to serve your application, write a script that imports
its ```wsgi.server``, as well as your app or app factory.
.. code-block:: python
:caption: ``wsgi.py``
import eventlet
from eventlet import wsgi
from hello import create_app
app = create_app()
wsgi.server(eventlet.listen(("127.0.0.1", 8000), app)
.. code-block:: text
$ python wsgi.py
(x) wsgi starting up on http://127.0.0.1:8000
Binding Externally
------------------
eventlet should not be run as root because it would cause your
application code to run as root, which is not secure. However, this
means it will not be possible to bind to port 80 or 443. Instead, a
reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
in front of eventlet.
You can bind to all external IPs on a non-privileged port by using
``0.0.0.0`` in the server arguments shown in the previous section.
Don't do this when using a reverse proxy setup, otherwise it will be
possible to bypass the proxy.
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
IP address in your browser.

View file

@ -1,238 +0,0 @@
FastCGI
=======
FastCGI is a deployment option on servers like `nginx`_, `lighttpd`_, and
`cherokee`_; see :doc:`uwsgi` and :doc:`wsgi-standalone` for other options.
To use your WSGI application with any of them you will need a FastCGI
server first. The most popular one is `flup`_ which we will use for
this guide. Make sure to have it installed to follow along.
.. admonition:: Watch Out
Please make sure in advance that any ``app.run()`` calls you might
have in your application file are inside an ``if __name__ ==
'__main__':`` block or moved to a separate file. Just make sure it's
not called because this will always start a local WSGI server which
we do not want if we deploy that application to FastCGI.
Creating a `.fcgi` file
-----------------------
First you need to create the FastCGI server file. Let's call it
`yourapplication.fcgi`::
#!/usr/bin/python
from flup.server.fcgi import WSGIServer
from yourapplication import app
if __name__ == '__main__':
WSGIServer(app).run()
This is enough for Apache to work, however nginx and older versions of
lighttpd need a socket to be explicitly passed to communicate with the
FastCGI server. For that to work you need to pass the path to the
socket to the :class:`~flup.server.fcgi.WSGIServer`::
WSGIServer(application, bindAddress='/path/to/fcgi.sock').run()
The path has to be the exact same path you define in the server
config.
Save the :file:`yourapplication.fcgi` file somewhere you will find it again.
It makes sense to have that in :file:`/var/www/yourapplication` or something
similar.
Make sure to set the executable bit on that file so that the servers
can execute it:
.. sourcecode:: text
$ chmod +x /var/www/yourapplication/yourapplication.fcgi
Configuring Apache
------------------
The example above is good enough for a basic Apache deployment but your
`.fcgi` file will appear in your application URL e.g.
``example.com/yourapplication.fcgi/news/``. There are few ways to configure
your application so that yourapplication.fcgi does not appear in the URL.
A preferable way is to use the ScriptAlias and SetHandler configuration
directives to route requests to the FastCGI server. The following example
uses FastCgiServer to start 5 instances of the application which will
handle all incoming requests::
LoadModule fastcgi_module /usr/lib64/httpd/modules/mod_fastcgi.so
FastCgiServer /var/www/html/yourapplication/app.fcgi -idle-timeout 300 -processes 5
<VirtualHost *>
ServerName webapp1.mydomain.com
DocumentRoot /var/www/html/yourapplication
AddHandler fastcgi-script fcgi
ScriptAlias / /var/www/html/yourapplication/app.fcgi/
<Location />
SetHandler fastcgi-script
</Location>
</VirtualHost>
These processes will be managed by Apache. If you're using a standalone
FastCGI server, you can use the FastCgiExternalServer directive instead.
Note that in the following the path is not real, it's simply used as an
identifier to other
directives such as AliasMatch::
FastCgiServer /var/www/html/yourapplication -host 127.0.0.1:3000
If you cannot set ScriptAlias, for example on a shared web host, you can use
WSGI middleware to remove yourapplication.fcgi from the URLs. Set .htaccess::
<IfModule mod_fcgid.c>
AddHandler fcgid-script .fcgi
<Files ~ (\.fcgi)>
SetHandler fcgid-script
Options +FollowSymLinks +ExecCGI
</Files>
</IfModule>
<IfModule mod_rewrite.c>
Options +FollowSymlinks
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ yourapplication.fcgi/$1 [QSA,L]
</IfModule>
Set yourapplication.fcgi::
#!/usr/bin/python
#: optional path to your local python site-packages folder
import sys
sys.path.insert(0, '<your_local_path>/lib/python<your_python_version>/site-packages')
from flup.server.fcgi import WSGIServer
from yourapplication import app
class ScriptNameStripper(object):
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
environ['SCRIPT_NAME'] = ''
return self.app(environ, start_response)
app = ScriptNameStripper(app)
if __name__ == '__main__':
WSGIServer(app).run()
Configuring lighttpd
--------------------
A basic FastCGI configuration for lighttpd looks like that::
fastcgi.server = ("/yourapplication.fcgi" =>
((
"socket" => "/tmp/yourapplication-fcgi.sock",
"bin-path" => "/var/www/yourapplication/yourapplication.fcgi",
"check-local" => "disable",
"max-procs" => 1
))
)
alias.url = (
"/static/" => "/path/to/your/static/"
)
url.rewrite-once = (
"^(/static($|/.*))$" => "$1",
"^(/.*)$" => "/yourapplication.fcgi$1"
)
Remember to enable the FastCGI, alias and rewrite modules. This configuration
binds the application to ``/yourapplication``. If you want the application to
work in the URL root you have to work around a lighttpd bug with the
:class:`~werkzeug.contrib.fixers.LighttpdCGIRootFix` middleware.
Make sure to apply it only if you are mounting the application the URL
root. Also, see the Lighty docs for more information on `FastCGI and Python
<https://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModFastCGI>`_ (note that
explicitly passing a socket to run() is no longer necessary).
Configuring nginx
-----------------
Installing FastCGI applications on nginx is a bit different because by
default no FastCGI parameters are forwarded.
A basic Flask FastCGI configuration for nginx looks like this::
location = /yourapplication { rewrite ^ /yourapplication/ last; }
location /yourapplication { try_files $uri @yourapplication; }
location @yourapplication {
include fastcgi_params;
fastcgi_split_path_info ^(/yourapplication)(.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_pass unix:/tmp/yourapplication-fcgi.sock;
}
This configuration binds the application to ``/yourapplication``. If you
want to have it in the URL root it's a bit simpler because you don't
have to figure out how to calculate ``PATH_INFO`` and ``SCRIPT_NAME``::
location / { try_files $uri @yourapplication; }
location @yourapplication {
include fastcgi_params;
fastcgi_param PATH_INFO $fastcgi_script_name;
fastcgi_param SCRIPT_NAME "";
fastcgi_pass unix:/tmp/yourapplication-fcgi.sock;
}
Running FastCGI Processes
-------------------------
Since nginx and others do not load FastCGI apps, you have to do it by
yourself. `Supervisor can manage FastCGI processes.
<http://supervisord.org/configuration.html#fcgi-program-x-section-settings>`_
You can look around for other FastCGI process managers or write a script
to run your `.fcgi` file at boot, e.g. using a SysV ``init.d`` script.
For a temporary solution, you can always run the ``.fcgi`` script inside
GNU screen. See ``man screen`` for details, and note that this is a
manual solution which does not persist across system restart::
$ screen
$ /var/www/yourapplication/yourapplication.fcgi
Debugging
---------
FastCGI deployments tend to be hard to debug on most web servers. Very
often the only thing the server log tells you is something along the
lines of "premature end of headers". In order to debug the application
the only thing that can really give you ideas why it breaks is switching
to the correct user and executing the application by hand.
This example assumes your application is called `application.fcgi` and
that your web server user is `www-data`::
$ su www-data
$ cd /var/www/yourapplication
$ python application.fcgi
Traceback (most recent call last):
File "yourapplication.fcgi", line 4, in <module>
ImportError: No module named yourapplication
In this case the error seems to be "yourapplication" not being on the
python path. Common problems are:
- Relative paths being used. Don't rely on the current working directory.
- The code depending on environment variables that are not set by the
web server.
- Different python interpreters being used.
.. _nginx: https://nginx.org/
.. _lighttpd: https://www.lighttpd.net/
.. _cherokee: https://cherokee-project.com/
.. _flup: https://pypi.org/project/flup/

80
docs/deploying/gevent.rst Normal file
View file

@ -0,0 +1,80 @@
gevent
======
Prefer using :doc:`gunicorn` or :doc:`uwsgi` with gevent workers rather
than using `gevent`_ directly. Gunicorn and uWSGI provide much more
configurable and production-tested servers.
`gevent`_ allows writing asynchronous, coroutine-based code that looks
like standard synchronous Python. It uses `greenlet`_ to enable task
switching without writing ``async/await`` or using ``asyncio``.
:doc:`eventlet` is another library that does the same thing. Certain
dependencies you have, or other considerations, may affect which of the
two you choose to use.
gevent provides a WSGI server that can handle many connections at once
instead of one per worker process. You must actually use gevent in your
own code to see any benefit to using the server.
.. _gevent: https://www.gevent.org/
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
Installing
----------
When using gevent, greenlet>=1.0 is required, otherwise context locals
such as ``request`` will not work as expected. When using PyPy,
PyPy>=7.3.7 is required.
Create a virtualenv, install your application, then install ``gevent``.
.. code-block:: text
$ cd hello-app
$ python -m venv venv
$ . venv/bin/activate
$ pip install . # install your application
$ pip install gevent
Running
-------
To use gevent to serve your application, write a script that imports its
``WSGIServer``, as well as your app or app factory.
.. code-block:: python
:caption: ``wsgi.py``
from gevent.pywsgi import WSGIServer
from hello import create_app
app = create_app()
http_server = WSGIServer(("127.0.0.1", 8000), app)
http_server.serve_forever()
.. code-block:: text
$ python wsgi.py
No output is shown when the server starts.
Binding Externally
------------------
gevent should not be run as root because it would cause your
application code to run as root, which is not secure. However, this
means it will not be possible to bind to port 80 or 443. Instead, a
reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
in front of gevent.
You can bind to all external IPs on a non-privileged port by using
``0.0.0.0`` in the server arguments shown in the previous section. Don't
do this when using a reverse proxy setup, otherwise it will be possible
to bypass the proxy.
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
IP address in your browser.

130
docs/deploying/gunicorn.rst Normal file
View file

@ -0,0 +1,130 @@
Gunicorn
========
`Gunicorn`_ is a pure Python WSGI server with simple configuration and
multiple worker implementations for performance tuning.
* It tends to integrate easily with hosting platforms.
* It does not support Windows (but does run on WSL).
* It is easy to install as it does not require additional dependencies
or compilation.
* It has built-in async worker support using gevent or eventlet.
This page outlines the basics of running Gunicorn. Be sure to read its
`documentation`_ and use ``gunicorn --help`` to understand what features
are available.
.. _Gunicorn: https://gunicorn.org/
.. _documentation: https://docs.gunicorn.org/
Installing
----------
Gunicorn is easy to install, as it does not require external
dependencies or compilation. It runs on Windows only under WSL.
Create a virtualenv, install your application, then install
``gunicorn``.
.. code-block:: text
$ cd hello-app
$ python -m venv venv
$ . venv/bin/activate
$ pip install . # install your application
$ pip install gunicorn
Running
-------
The only required argument to Gunicorn tells it how to load your Flask
application. The syntax is ``{module_import}:{app_variable}``.
``module_import`` is the dotted import name to the module with your
application. ``app_variable`` is the variable with the application. It
can also be a function call (with any arguments) if you're using the
app factory pattern.
.. code-block:: text
# equivalent to 'from hello import app'
$ gunicorn -w 4 'hello:app'
# equivalent to 'from hello import create_app; create_app()'
$ gunicorn -w 4 'hello:create_app()'
Starting gunicorn 20.1.0
Listening at: http://127.0.0.1:8000 (x)
Using worker: sync
Booting worker with pid: x
Booting worker with pid: x
Booting worker with pid: x
Booting worker with pid: x
The ``-w`` option specifies the number of processes to run; a starting
value could be ``CPU * 2``. The default is only 1 worker, which is
probably not what you want for the default worker type.
Logs for each request aren't shown by default, only worker info and
errors are shown. To show access logs on stdout, use the
``--access-logfile=-`` option.
Binding Externally
------------------
Gunicorn should not be run as root because it would cause your
application code to run as root, which is not secure. However, this
means it will not be possible to bind to port 80 or 443. Instead, a
reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
in front of Gunicorn.
You can bind to all external IPs on a non-privileged port using the
``-b 0.0.0.0`` option. Don't do this when using a reverse proxy setup,
otherwise it will be possible to bypass the proxy.
.. code-block:: text
$ gunicorn -w 4 -b 0.0.0.0 'hello:create_app()'
Listening at: http://0.0.0.0:8000 (x)
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
IP address in your browser.
Async with gevent or eventlet
-----------------------------
The default sync worker is appropriate for many use cases. If you need
asynchronous support, Gunicorn provides workers using either `gevent`_
or `eventlet`_. This is not the same as Python's ``async/await``, or the
ASGI server spec. You must actually use gevent/eventlet in your own code
to see any benefit to using the workers.
When using either gevent or eventlet, greenlet>=1.0 is required,
otherwise context locals such as ``request`` will not work as expected.
When using PyPy, PyPy>=7.3.7 is required.
To use gevent:
.. code-block:: text
$ gunicorn -k gevent 'hello:create_app()'
Starting gunicorn 20.1.0
Listening at: http://127.0.0.1:8000 (x)
Using worker: gevent
Booting worker with pid: x
To use eventlet:
.. code-block:: text
$ gunicorn -k eventlet 'hello:create_app()'
Starting gunicorn 20.1.0
Listening at: http://127.0.0.1:8000 (x)
Using worker: eventlet
Booting worker with pid: x
.. _gevent: https://www.gevent.org/
.. _eventlet: https://eventlet.net/

View file

@ -1,35 +1,80 @@
Deployment Options
==================
Deploying to Production
=======================
While lightweight and easy to use, **Flask's built-in server is not suitable
for production** as it doesn't scale well. Some of the options available for
properly running Flask in production are documented here.
After developing your application, you'll want to make it available
publicly to other users. When you're developing locally, you're probably
using the built-in development server, debugger, and reloader. These
should not be used in production. Instead, you should use a dedicated
WSGI server or hosting platform, some of which will be described here.
If you want to deploy your Flask application to a WSGI server not listed here,
look up the server documentation about how to use a WSGI app with it. Just
remember that your :class:`Flask` application object is the actual WSGI
application.
"Production" means "not development", which applies whether you're
serving your application publicly to millions of users or privately /
locally to a single user. **Do not use the development server when
deploying to production. It is intended for use only during local
development. It is not designed to be particularly secure, stable, or
efficient.**
Hosted options
--------------
- `Deploying Flask on Heroku <https://devcenter.heroku.com/articles/getting-started-with-python>`_
- `Deploying Flask on Google App Engine <https://cloud.google.com/appengine/docs/standard/python3/runtime>`_
- `Deploying Flask on Google Cloud Run <https://cloud.google.com/run/docs/quickstarts/build-and-deploy/python>`_
- `Deploying Flask on AWS Elastic Beanstalk <https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create-deploy-python-flask.html>`_
- `Deploying on Azure (IIS) <https://docs.microsoft.com/en-us/azure/app-service/containers/how-to-configure-python>`_
- `Deploying on PythonAnywhere <https://help.pythonanywhere.com/pages/Flask/>`_
Self-hosted options
Self-Hosted Options
-------------------
.. toctree::
:maxdepth: 2
Flask is a WSGI *application*. A WSGI *server* is used to run the
application, converting incoming HTTP requests to the standard WSGI
environ, and converting outgoing WSGI responses to HTTP responses.
wsgi-standalone
uwsgi
mod_wsgi
fastcgi
cgi
asgi
The primary goal of these docs is to familiarize you with the concepts
involved in running a WSGI application using a production WSGI server
and HTTP server. There are many WSGI servers and HTTP servers, with many
configuration possibilities. The pages below discuss the most common
servers, and show the basics of running each one. The next section
discusses platforms that can manage this for you.
.. toctree::
:maxdepth: 1
gunicorn
waitress
mod_wsgi
uwsgi
gevent
eventlet
asgi
WSGI servers have HTTP servers built-in. However, a dedicated HTTP
server may be safer, more efficient, or more capable. Putting an HTTP
server in front of the WSGI server is called a "reverse proxy."
.. toctree::
:maxdepth: 1
proxy_fix
nginx
apache-httpd
This list is not exhaustive, and you should evaluate these and other
servers based on your application's needs. Different servers will have
different capabilities, configuration, and support.
Hosting Platforms
-----------------
There are many services available for hosting web applications without
needing to maintain your own server, networking, domain, etc. Some
services may have a free tier up to a certain time or bandwidth. Many of
these services use one of the WSGI servers described above, or a similar
interface. The links below are for some of the most common platforms,
which have instructions for Flask, WSGI, or Python.
- `PythonAnywhere <https://help.pythonanywhere.com/pages/Flask/>`_
- `Heroku <https://devcenter.heroku.com/articles/getting-started-with-python>`_
- `Google App Engine <https://cloud.google.com/appengine/docs/standard/python3/building-app>`_
- `Google Cloud Run <https://cloud.google.com/run/docs/quickstarts/build-and-deploy/deploy-python-service>`_
- `AWS Elastic Beanstalk <https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create-deploy-python-flask.html>`_
- `Microsoft Azure <https://docs.microsoft.com/en-us/azure/app-service/quickstart-python>`_
This list is not exhaustive, and you should evaluate these and other
services based on your application's needs. Different services will have
different capabilities, configuration, pricing, and support.
You'll probably need to :doc:`proxy_fix` when using most hosting
platforms.

View file

@ -1,216 +1,105 @@
mod_wsgi (Apache)
=================
mod_wsgi
========
If you are using the `Apache`_ webserver, consider using `mod_wsgi`_.
`mod_wsgi`_ is a WSGI server integrated with the `Apache httpd`_ server.
The modern `mod_wsgi-express`_ command makes it easy to configure and
start the server without needing to write Apache httpd configuration.
.. admonition:: Watch Out
* Tightly integrated with Apache httpd.
* Supports Windows directly.
* Requires a compiler to install. Requires Apache installed separately
on Windows.
* Does not require a reverse proxy setup.
Please make sure in advance that any ``app.run()`` calls you might
have in your application file are inside an ``if __name__ ==
'__main__':`` block or moved to a separate file. Just make sure it's
not called because this will always start a local WSGI server which
we do not want if we deploy that application to mod_wsgi.
This page outlines the basics of running mod_wsgi-express, not the more
complex installation and configuration with httpd. Be sure to read the
`mod_wsgi-express`_, `mod_wsgi`_, and `Apache httpd`_ documentation to
understand what features are available.
.. _Apache: https://httpd.apache.org/
.. _mod_wsgi-express: https://pypi.org/project/mod-wsgi/
.. _mod_wsgi: https://modwsgi.readthedocs.io/
.. _Apache httpd: https://httpd.apache.org/
Installing `mod_wsgi`
---------------------
If you don't have `mod_wsgi` installed yet you have to either install it
using a package manager or compile it yourself. The mod_wsgi
`installation instructions`_ cover source installations on UNIX systems.
Installing
----------
If you are using Ubuntu/Debian you can apt-get it and activate it as
follows:
On Linux/Mac, the most straightforward way to install mod_wsgi is to
install the ``mod_wsgi-standalone`` package, which will compile an
up-to-date version of Apache httpd as well.
.. sourcecode:: text
Create a virtualenv, install your application, then install
``mod_wsgi-standalone``.
$ apt-get install libapache2-mod-wsgi-py3
.. code-block:: text
If you are using a yum based distribution (Fedora, OpenSUSE, etc..) you
can install it as follows:
$ cd hello-app
$ python -m venv venv
$ . venv/bin/activate
$ pip install . # install your application
$ pip install mod_wsgi-standalone
.. sourcecode:: text
If you want to use the system-installed version of Apache httpd
(required on Windows, optional but faster on Linux/Mac), install the
``mod_wsgi`` package instead. You will get an error if Apache and its
development headers are not available. How to install them depends on
what OS and package manager you use.
$ yum install mod_wsgi
.. code-block:: text
On FreeBSD install `mod_wsgi` by compiling the `www/mod_wsgi` port or by
using pkg_add:
$ pip install mod_wsgi
.. sourcecode:: text
$ pkg install ap24-py37-mod_wsgi
Running
-------
If you are using pkgsrc you can install `mod_wsgi` by compiling the
`www/ap2-wsgi` package.
The only argument to ``mod_wsgi-express`` specifies a script containing
your Flask application, which must be called ``application``. You can
write a small script to import your app with this name, or to create it
if using the app factory pattern.
If you encounter segfaulting child processes after the first apache
reload you can safely ignore them. Just restart the server.
.. code-block:: python
:caption: ``wsgi.py``
Creating a `.wsgi` file
-----------------------
from hello import app
To run your application you need a :file:`yourapplication.wsgi` file.
This file contains the code `mod_wsgi` is executing on startup
to get the application object. The object called `application`
in that file is then used as application.
application = app
For most applications the following file should be sufficient::
.. code-block:: python
:caption: ``wsgi.py``
from yourapplication import app as application
from hello import create_app
If a factory function is used in a :file:`__init__.py` file, then the function should be imported::
from yourapplication import create_app
application = create_app()
If you don't have a factory function for application creation but a singleton
instance you can directly import that one as `application`.
Now run the ``mod_wsgi-express start-server`` command.
Store that file somewhere that you will find it again (e.g.:
:file:`/var/www/yourapplication`) and make sure that `yourapplication` and all
the libraries that are in use are on the python load path. If you don't
want to install it system wide consider using a `virtual python`_
instance. Keep in mind that you will have to actually install your
application into the virtualenv as well. Alternatively there is the
option to just patch the path in the ``.wsgi`` file before the import::
.. code-block:: text
import sys
sys.path.insert(0, '/path/to/the/application')
$ mod_wsgi-express start-server wsgi.py --processes 4
Configuring Apache
The ``--processes`` option specifies the number of worker processes to
run; a starting value could be ``CPU * 2``.
Logs for each request aren't show in the terminal. If an error occurs,
its information is written to the error log file shown when starting the
server.
Binding Externally
------------------
The last thing you have to do is to create an Apache configuration file
for your application. In this example we are telling `mod_wsgi` to
execute the application under a different user for security reasons:
Unlike the other WSGI servers in these docs, mod_wsgi can be run as
root to bind to privileged ports like 80 and 443. However, it must be
configured to drop permissions to a different user and group for the
worker processes.
.. sourcecode:: apache
For example, if you created a ``hello`` user and group, you should
install your virtualenv and application as that user, then tell
mod_wsgi to drop to that user after starting.
<VirtualHost *>
ServerName example.com
.. code-block:: text
WSGIDaemonProcess yourapplication user=user1 group=group1 threads=5
WSGIScriptAlias / /var/www/yourapplication/yourapplication.wsgi
<Directory /var/www/yourapplication>
WSGIProcessGroup yourapplication
WSGIApplicationGroup %{GLOBAL}
Order deny,allow
Allow from all
</Directory>
</VirtualHost>
Note: WSGIDaemonProcess isn't implemented in Windows and Apache will
refuse to run with the above configuration. On a Windows system, eliminate those lines:
.. sourcecode:: apache
<VirtualHost *>
ServerName example.com
WSGIScriptAlias / C:\yourdir\yourapp.wsgi
<Directory C:\yourdir>
Order deny,allow
Allow from all
</Directory>
</VirtualHost>
Note: There have been some changes in access control configuration
for `Apache 2.4`_.
.. _Apache 2.4: https://httpd.apache.org/docs/trunk/upgrading.html
Most notably, the syntax for directory permissions has changed from httpd 2.2
.. sourcecode:: apache
Order allow,deny
Allow from all
to httpd 2.4 syntax
.. sourcecode:: apache
Require all granted
For more information consult the `mod_wsgi documentation`_.
.. _mod_wsgi: https://github.com/GrahamDumpleton/mod_wsgi
.. _installation instructions: https://modwsgi.readthedocs.io/en/develop/installation.html
.. _virtual python: https://pypi.org/project/virtualenv/
.. _mod_wsgi documentation: https://modwsgi.readthedocs.io/en/develop/index.html
Troubleshooting
---------------
If your application does not run, follow this guide to troubleshoot:
**Problem:** application does not run, errorlog shows SystemExit ignored
You have an ``app.run()`` call in your application file that is not
guarded by an ``if __name__ == '__main__':`` condition. Either
remove that :meth:`~flask.Flask.run` call from the file and move it
into a separate :file:`run.py` file or put it into such an if block.
**Problem:** application gives permission errors
Probably caused by your application running as the wrong user. Make
sure the folders the application needs access to have the proper
privileges set and the application runs as the correct user
(``user`` and ``group`` parameter to the `WSGIDaemonProcess`
directive)
**Problem:** application dies with an error on print
Keep in mind that mod_wsgi disallows doing anything with
:data:`sys.stdout` and :data:`sys.stderr`. You can disable this
protection from the config by setting the `WSGIRestrictStdout` to
``off``:
.. sourcecode:: apache
WSGIRestrictStdout Off
Alternatively you can also replace the standard out in the .wsgi file
with a different stream::
import sys
sys.stdout = sys.stderr
**Problem:** accessing resources gives IO errors
Your application probably is a single .py file you symlinked into
the site-packages folder. Please be aware that this does not work,
instead you either have to put the folder into the pythonpath the
file is stored in, or convert your application into a package.
The reason for this is that for non-installed packages, the module
filename is used to locate the resources and for symlinks the wrong
filename is picked up.
Support for Automatic Reloading
-------------------------------
To help deployment tools you can activate support for automatic
reloading. Whenever something changes the ``.wsgi`` file, `mod_wsgi` will
reload all the daemon processes for us.
For that, just add the following directive to your `Directory` section:
.. sourcecode:: apache
WSGIScriptReloading On
Working with Virtual Environments
---------------------------------
Virtual environments have the advantage that they never install the
required dependencies system wide so you have a better control over what
is used where. If you want to use a virtual environment with mod_wsgi
you have to modify your ``.wsgi`` file slightly.
Add the following lines to the top of your ``.wsgi`` file::
activate_this = '/path/to/env/bin/activate_this.py'
with open(activate_this) as file_:
exec(file_.read(), dict(__file__=activate_this))
This sets up the load paths according to the settings of the virtual
environment. Keep in mind that the path has to be absolute.
$ sudo /home/hello/venv/bin/mod_wsgi-express start-server \
/home/hello/wsgi.py \
--user hello --group hello --port 80 --processes 4

69
docs/deploying/nginx.rst Normal file
View file

@ -0,0 +1,69 @@
nginx
=====
`nginx`_ is a fast, production level HTTP server. When serving your
application with one of the WSGI servers listed in :doc:`index`, it is
often good or necessary to put a dedicated HTTP server in front of it.
This "reverse proxy" can handle incoming requests, TLS, and other
security and performance concerns better than the WSGI server.
Nginx can be installed using your system package manager, or a pre-built
executable for Windows. Installing and running Nginx itself is outside
the scope of this doc. This page outlines the basics of configuring
Nginx to proxy your application. Be sure to read its documentation to
understand what features are available.
.. _nginx: https://nginx.org/
Domain Name
-----------
Acquiring and configuring a domain name is outside the scope of this
doc. In general, you will buy a domain name from a registrar, pay for
server space with a hosting provider, and then point your registrar
at the hosting provider's name servers.
To simulate this, you can also edit your ``hosts`` file, located at
``/etc/hosts`` on Linux. Add a line that associates a name with the
local IP.
Modern Linux systems may be configured to treat any domain name that
ends with ``.localhost`` like this without adding it to the ``hosts``
file.
.. code-block:: python
:caption: ``/etc/hosts``
127.0.0.1 hello.localhost
Configuration
-------------
The nginx configuration is located at ``/etc/nginx/nginx.conf`` on
Linux. It may be different depending on your operating system. Check the
docs and look for ``nginx.conf``.
Remove or comment out any existing ``server`` section. Add a ``server``
section and use the ``proxy_pass`` directive to point to the address the
WSGI server is listening on. We'll assume the WSGI server is listening
locally at ``http://127.0.0.1:8000``.
.. code-block:: nginx
:caption: ``/etc/nginx.conf``
server {
listen 80;
server_name _;
location / {
proxy_pass http://127.0.0.1:8000/;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Prefix /;
}
}
Then :doc:`proxy_fix` so that your application uses these headers.

View file

@ -0,0 +1,33 @@
Tell Flask it is Behind a Proxy
===============================
When using a reverse proxy, or many Python hosting platforms, the proxy
will intercept and forward all external requests to the local WSGI
server.
From the WSGI server and Flask application's perspectives, requests are
now coming from the HTTP server to the local address, rather than from
the remote address to the external server address.
HTTP servers should set ``X-Forwarded-`` headers to pass on the real
values to the application. The application can then be told to trust and
use those values by wrapping it with the
:doc:`werkzeug:middleware/proxy_fix` middleware provided by Werkzeug.
This middleware should only be used if the application is actually
behind a proxy, and should be configured with the number of proxies that
are chained in front of it. Not all proxies set all the headers. Since
incoming headers can be faked, you must set how many proxies are setting
each header so the middleware knows what to trust.
.. code-block:: python
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(
app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1
)
Remember, only apply this middleware if you are behind a proxy, and set
the correct number of proxies that set each header. It can be a security
issue if you get this configuration wrong.

View file

@ -1,71 +1,145 @@
uWSGI
=====
uWSGI is a deployment option on servers like `nginx`_, `lighttpd`_, and
`cherokee`_; see :doc:`fastcgi` and :doc:`wsgi-standalone` for other options.
To use your WSGI application with uWSGI protocol you will need a uWSGI server
first. uWSGI is both a protocol and an application server; the application
server can serve uWSGI, FastCGI, and HTTP protocols.
`uWSGI`_ is a fast, compiled server suite with extensive configuration
and capabilities beyond a basic server.
The most popular uWSGI server is `uwsgi`_, which we will use for this
guide. Make sure to have it installed to follow along.
* It can be very performant due to being a compiled program.
* It is complex to configure beyond the basic application, and has so
many options that it can be difficult for beginners to understand.
* It does not support Windows (but does run on WSL).
* It requires a compiler to install in some cases.
.. admonition:: Watch Out
This page outlines the basics of running uWSGI. Be sure to read its
documentation to understand what features are available.
Please make sure in advance that any ``app.run()`` calls you might
have in your application file are inside an ``if __name__ ==
'__main__':`` block or moved to a separate file. Just make sure it's
not called because this will always start a local WSGI server which
we do not want if we deploy that application to uWSGI.
.. _uWSGI: https://uwsgi-docs.readthedocs.io/en/latest/
Starting your app with uwsgi
----------------------------
`uwsgi` is designed to operate on WSGI callables found in python modules.
Installing
----------
Given a flask application in myapp.py, use the following command:
uWSGI has multiple ways to install it. The most straightforward is to
install the ``pyuwsgi`` package, which provides precompiled wheels for
common platforms. However, it does not provide SSL support, which can be
provided with a reverse proxy instead.
.. sourcecode:: text
Create a virtualenv, install your application, then install ``pyuwsgi``.
$ uwsgi -s /tmp/yourapplication.sock --manage-script-name --mount /yourapplication=myapp:app
.. code-block:: text
The ``--manage-script-name`` will move the handling of ``SCRIPT_NAME``
to uwsgi, since it is smarter about that.
It is used together with the ``--mount`` directive which will make
requests to ``/yourapplication`` be directed to ``myapp:app``.
If your application is accessible at root level, you can use a
single ``/`` instead of ``/yourapplication``. ``myapp`` refers to the name of
the file of your flask application (without extension) or the module which
provides ``app``. ``app`` is the callable inside of your application (usually
the line reads ``app = Flask(__name__)``).
$ cd hello-app
$ python -m venv venv
$ . venv/bin/activate
$ pip install . # install your application
$ pip install pyuwsgi
If you want to deploy your flask application inside of a virtual environment,
you need to also add ``--virtualenv /path/to/virtual/environment``. You might
also need to add ``--plugin python`` or ``--plugin python3`` depending on which
python version you use for your project.
If you have a compiler available, you can install the ``uwsgi`` package
instead. Or install the ``pyuwsgi`` package from sdist instead of wheel.
Either method will include SSL support.
Configuring nginx
.. code-block:: text
$ pip install uwsgi
# or
$ pip install --no-binary pyuwsgi pyuwsgi
Running
-------
The most basic way to run uWSGI is to tell it to start an HTTP server
and import your application.
.. code-block:: text
$ uwsgi --http 127.0.0.1:8000 --master -p 4 -w hello:app
*** Starting uWSGI 2.0.20 (64bit) on [x] ***
*** Operational MODE: preforking ***
mounting hello:app on /
spawned uWSGI master process (pid: x)
spawned uWSGI worker 1 (pid: x, cores: 1)
spawned uWSGI worker 2 (pid: x, cores: 1)
spawned uWSGI worker 3 (pid: x, cores: 1)
spawned uWSGI worker 4 (pid: x, cores: 1)
spawned uWSGI http 1 (pid: x)
If you're using the app factory pattern, you'll need to create a small
Python file to create the app, then point uWSGI at that.
.. code-block:: python
:caption: ``wsgi.py``
from hello import create_app
app = create_app()
.. code-block:: text
$ uwsgi --http 127.0.0.1:8000 --master -p 4 -w wsgi:app
The ``--http`` option starts an HTTP server at 127.0.0.1 port 8000. The
``--master`` option specifies the standard worker manager. The ``-p``
option starts 4 worker processes; a starting value could be ``CPU * 2``.
The ``-w`` option tells uWSGI how to import your application
Binding Externally
------------------
uWSGI should not be run as root with the configuration shown in this doc
because it would cause your application code to run as root, which is
not secure. However, this means it will not be possible to bind to port
80 or 443. Instead, a reverse proxy such as :doc:`nginx` or
:doc:`apache-httpd` should be used in front of uWSGI. It is possible to
run uWSGI as root securely, but that is beyond the scope of this doc.
uWSGI has optimized integration with `Nginx uWSGI`_ and
`Apache mod_proxy_uwsgi`_, and possibly other servers, instead of using
a standard HTTP proxy. That configuration is beyond the scope of this
doc, see the links for more information.
.. _Nginx uWSGI: https://uwsgi-docs.readthedocs.io/en/latest/Nginx.html
.. _Apache mod_proxy_uwsgi: https://uwsgi-docs.readthedocs.io/en/latest/Apache.html#mod-proxy-uwsgi
You can bind to all external IPs on a non-privileged port using the
``--http 0.0.0.0:8000`` option. Don't do this when using a reverse proxy
setup, otherwise it will be possible to bypass the proxy.
.. code-block:: text
$ uwsgi --http 0.0.0.0:8000 --master -p 4 -w wsgi:app
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
IP address in your browser.
Async with gevent
-----------------
A basic flask nginx configuration looks like this::
The default sync worker is appropriate for many use cases. If you need
asynchronous support, uWSGI provides a `gevent`_ worker. This is not the
same as Python's ``async/await``, or the ASGI server spec. You must
actually use gevent in your own code to see any benefit to using the
worker.
location = /yourapplication { rewrite ^ /yourapplication/; }
location /yourapplication { try_files $uri @yourapplication; }
location @yourapplication {
include uwsgi_params;
uwsgi_pass unix:/tmp/yourapplication.sock;
}
When using gevent, greenlet>=1.0 is required, otherwise context locals
such as ``request`` will not work as expected. When using PyPy,
PyPy>=7.3.7 is required.
This configuration binds the application to ``/yourapplication``. If you want
to have it in the URL root its a bit simpler::
.. code-block:: text
location / { try_files $uri @yourapplication; }
location @yourapplication {
include uwsgi_params;
uwsgi_pass unix:/tmp/yourapplication.sock;
}
$ uwsgi --http 127.0.0.1:8000 --master --gevent 100 -w wsgi:app
.. _nginx: https://nginx.org/
.. _lighttpd: https://www.lighttpd.net/
.. _cherokee: https://cherokee-project.com/
.. _uwsgi: https://uwsgi-docs.readthedocs.io/en/latest/
*** Starting uWSGI 2.0.20 (64bit) on [x] ***
*** Operational MODE: async ***
mounting hello:app on /
spawned uWSGI master process (pid: x)
spawned uWSGI worker 1 (pid: x, cores: 100)
spawned uWSGI http 1 (pid: x)
*** running gevent loop engine [addr:x] ***
.. _gevent: https://www.gevent.org/

View file

@ -0,0 +1,75 @@
Waitress
========
`Waitress`_ is a pure Python WSGI server.
* It is easy to configure.
* It supports Windows directly.
* It is easy to install as it does not require additional dependencies
or compilation.
* It does not support streaming requests, full request data is always
buffered.
* It uses a single process with multiple thread workers.
This page outlines the basics of running Waitress. Be sure to read its
documentation and ``waitress-serve --help`` to understand what features
are available.
.. _Waitress: https://docs.pylonsproject.org/projects/waitress/
Installing
----------
Create a virtualenv, install your application, then install
``waitress``.
.. code-block:: text
$ cd hello-app
$ python -m venv venv
$ . venv/bin/activate
$ pip install . # install your application
$ pip install waitress
Running
-------
The only required argument to ``waitress-serve`` tells it how to load
your Flask application. The syntax is ``{module}:{app}``. ``module`` is
the dotted import name to the module with your application. ``app`` is
the variable with the application. If you're using the app factory
pattern, use ``--call {module}:{factory}`` instead.
.. code-block:: text
# equivalent to 'from hello import app'
$ waitress-serve hello:app --host 127.0.0.1
# equivalent to 'from hello import create_app; create_app()'
$ waitress-serve --call hello:create_app --host 127.0.0.1
Serving on http://127.0.0.1:8080
The ``--host`` option binds the server to local ``127.0.0.1`` only.
Logs for each request aren't shown, only errors are shown. Logging can
be configured through the Python interface instead of the command line.
Binding Externally
------------------
Waitress should not be run as root because it would cause your
application code to run as root, which is not secure. However, this
means it will not be possible to bind to port 80 or 443. Instead, a
reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
in front of Waitress.
You can bind to all external IPs on a non-privileged port by not
specifying the ``--host`` option. Don't do this when using a revers
proxy setup, otherwise it will be possible to bypass the proxy.
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
IP address in your browser.

View file

@ -1,262 +0,0 @@
Standalone WSGI Servers
=======================
Most WSGI servers also provide HTTP servers, so they can run a WSGI
application and make it available externally.
It may still be a good idea to run the server behind a dedicated HTTP
server such as Apache or Nginx. See :ref:`deploying-proxy-setups` if you
run into issues with that.
Gunicorn
--------
`Gunicorn`_ is a WSGI and HTTP server for UNIX. To run a Flask
application, tell Gunicorn how to import your Flask app object.
.. code-block:: text
$ gunicorn -w 4 -b 0.0.0.0:5000 your_project:app
The ``-w 4`` option uses 4 workers to handle 4 requests at once. The
``-b 0.0.0.0:5000`` serves the application on all interfaces on port
5000.
Gunicorn provides many options for configuring the server, either
through a configuration file or with command line options. Use
``gunicorn --help`` or see the docs for more information.
The command expects the name of your module or package to import and
the application instance within the module. If you use the application
factory pattern, you can pass a call to that.
.. code-block:: text
$ gunicorn -w 4 -b 0.0.0.0:5000 "myproject:create_app()"
Async with Gevent or Eventlet
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The default sync worker is appropriate for many use cases. If you need
asynchronous support, Gunicorn provides workers using either `gevent`_
or `eventlet`_. This is not the same as Python's ``async/await``, or the
ASGI server spec.
When using either gevent or eventlet, greenlet>=1.0 is required,
otherwise context locals such as ``request`` will not work as expected.
When using PyPy, PyPy>=7.3.7 is required.
To use gevent:
.. code-block:: text
$ gunicorn -k gevent -b 0.0.0.0:5000 your_project:app
To use eventlet:
.. code-block:: text
$ gunicorn -k eventlet -b 0.0.0.0:5000 your_project:app
.. _Gunicorn: https://gunicorn.org/
.. _gevent: http://www.gevent.org/
.. _eventlet: https://eventlet.net/
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
uWSGI
-----
`uWSGI`_ is a fast application server written in C. It is very
configurable, which makes it more complicated to setup than Gunicorn.
It also provides many other utilities for writing robust web
applications. To run a Flask application, tell uWSGI how to import
your Flask app object.
.. code-block:: text
$ uwsgi --master -p 4 --http 0.0.0.0:5000 -w your_project:app
The ``-p 4`` option uses 4 workers to handle 4 requests at once. The
``--http 0.0.0.0:5000`` serves the application on all interfaces on port
5000.
uWSGI has optimized integration with Nginx and Apache instead of using
a standard HTTP proxy. See :doc:`configuring uWSGI and Nginx <uwsgi>`.
Async with Gevent
~~~~~~~~~~~~~~~~~
The default sync worker is appropriate for many use cases. If you need
asynchronous support, uWSGI provides workers using `gevent`_. It also
supports other async modes, see the docs for more information. This is
not the same as Python's ``async/await``, or the ASGI server spec.
When using gevent, greenlet>=1.0 is required, otherwise context locals
such as ``request`` will not work as expected. When using PyPy,
PyPy>=7.3.7 is required.
.. code-block:: text
$ uwsgi --master --gevent 100 --http 0.0.0.0:5000 -w your_project:app
.. _uWSGI: https://uwsgi-docs.readthedocs.io/en/latest/
Gevent
------
Prefer using `Gunicorn`_ with Gevent workers rather than using Gevent
directly. Gunicorn provides a much more configurable and
production-tested server. See the section on Gunicorn above.
`Gevent`_ allows writing asynchronous, coroutine-based code that looks
like standard synchronous Python. It uses `greenlet`_ to enable task
switching without writing ``async/await`` or using ``asyncio``.
It provides a WSGI server that can handle many connections at once
instead of one per worker process.
`Eventlet`_, described below, is another library that does the same
thing. Certain dependencies you have, or other consideration, may affect
which of the two you choose to use
To use gevent to serve your application, import its ``WSGIServer`` and
use it to run your ``app``.
.. code-block:: python
from gevent.pywsgi import WSGIServer
from your_project import app
http_server = WSGIServer(("", 5000), app)
http_server.serve_forever()
Eventlet
--------
Prefer using `Gunicorn`_ with Eventlet workers rather than using
Eventlet directly. Gunicorn provides a much more configurable and
production-tested server. See the section on Gunicorn above.
`Eventlet`_ allows writing asynchronous, coroutine-based code that looks
like standard synchronous Python. It uses `greenlet`_ to enable task
switching without writing ``async/await`` or using ``asyncio``.
It provides a WSGI server that can handle many connections at once
instead of one per worker process.
`Gevent`_, described above, is another library that does the same
thing. Certain dependencies you have, or other consideration, may affect
which of the two you choose to use
To use eventlet to serve your application, import its ``wsgi.server``
and use it to run your ``app``.
.. code-block:: python
import eventlet
from eventlet import wsgi
from your_project import app
wsgi.server(eventlet.listen(("", 5000), app)
Twisted Web
-----------
`Twisted Web`_ is the web server shipped with `Twisted`_, a mature,
non-blocking event-driven networking library. Twisted Web comes with a
standard WSGI container which can be controlled from the command line using
the ``twistd`` utility:
.. code-block:: text
$ twistd web --wsgi myproject.app
This example will run a Flask application called ``app`` from a module named
``myproject``.
Twisted Web supports many flags and options, and the ``twistd`` utility does
as well; see ``twistd -h`` and ``twistd web -h`` for more information. For
example, to run a Twisted Web server in the foreground, on port 8080, with an
application from ``myproject``:
.. code-block:: text
$ twistd -n web --port tcp:8080 --wsgi myproject.app
.. _Twisted: https://twistedmatrix.com/trac/
.. _Twisted Web: https://twistedmatrix.com/trac/wiki/TwistedWeb
.. _deploying-proxy-setups:
Proxy Setups
------------
If you deploy your application using one of these servers behind an HTTP proxy
you will need to rewrite a few headers in order for the application to work.
The two problematic values in the WSGI environment usually are ``REMOTE_ADDR``
and ``HTTP_HOST``. You can configure your httpd to pass these headers, or you
can fix them in middleware. Werkzeug ships a fixer that will solve some common
setups, but you might want to write your own WSGI middleware for specific
setups.
Here's a simple nginx configuration which proxies to an application served on
localhost at port 8000, setting appropriate headers:
.. sourcecode:: nginx
server {
listen 80;
server_name _;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location / {
proxy_pass http://127.0.0.1:8000/;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
If your httpd is not providing these headers, the most common setup invokes the
host being set from ``X-Forwarded-Host`` and the remote address from
``X-Forwarded-For``::
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)
.. admonition:: Trusting Headers
Please keep in mind that it is a security issue to use such a middleware in
a non-proxy setup because it will blindly trust the incoming headers which
might be forged by malicious clients.
If you want to rewrite the headers from another header, you might want to
use a fixer like this::
class CustomProxyFix(object):
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
host = environ.get('HTTP_X_FHOST', '')
if host:
environ['HTTP_HOST'] = host
return self.app(environ, start_response)
app.wsgi_app = CustomProxyFix(app.wsgi_app)