mirror of
https://github.com/kennethreitz/responder.git
synced 2026-10-24 05:59:12 +00:00
414 lines
31 KiB
HTML
414 lines
31 KiB
HTML
<!DOCTYPE html>
|
||
|
||
<html lang="en" data-content_root="./">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
|
||
<title>Deployment — responder 9.2.0 documentation</title>
|
||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=5ecbeea2" />
|
||
<link rel="stylesheet" type="text/css" href="_static/basic.css?v=b08954a9" />
|
||
<link rel="stylesheet" type="text/css" href="_static/alabaster.css?v=eb4102c5" />
|
||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
|
||
<link rel="stylesheet" type="text/css" href="_static/sphinx-design.min.css?v=95c83b7e" />
|
||
<link rel="stylesheet" type="text/css" href="_static/design-elements.e5416f61bae5d36adc6d722a2b6f8cff.css?v=452a8e97" />
|
||
<script src="_static/documentation_options.js?v=e8b940be"></script>
|
||
<script src="_static/doctools.js?v=fd6eb6e6"></script>
|
||
<script src="_static/sphinx_highlight.js?v=6ffebe34"></script>
|
||
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
|
||
<script src="_static/copybutton.js?v=fd10adb8"></script>
|
||
<script>
|
||
</script>
|
||
<script src="_static/design-tabs.js?v=f930bc37"></script>
|
||
<script src="_static/design-elements.bbdccc18c4abea9397628f9fea3d48c2.js?v=03c7770e"></script>
|
||
<link rel="index" title="Index" href="genindex.html" />
|
||
<link rel="search" title="Search" href="search.html" />
|
||
<link rel="next" title="Testing" href="testing.html" />
|
||
<link rel="prev" title="Generated Clients" href="clientgen.html" />
|
||
|
||
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
|
||
|
||
|
||
|
||
|
||
|
||
</head><body>
|
||
|
||
|
||
<div class="document">
|
||
<div class="documentwrapper">
|
||
<div class="bodywrapper">
|
||
|
||
|
||
<div class="body" role="main">
|
||
|
||
<section id="deployment">
|
||
<h1>Deployment<a class="headerlink" href="#deployment" title="Link to this heading">¶</a></h1>
|
||
<p>Responder applications are standard <a class="reference external" href="https://asgi.readthedocs.io/">ASGI</a>
|
||
apps. ASGI (Asynchronous Server Gateway Interface) is the modern successor
|
||
to WSGI — it supports async, WebSockets, and HTTP/2. This means you can
|
||
deploy a Responder app anywhere that runs Python, using any ASGI server.</p>
|
||
<section id="running-locally">
|
||
<h2>Running Locally<a class="headerlink" href="#running-locally" title="Link to this heading">¶</a></h2>
|
||
<p>During development, <code class="docutils literal notranslate"><span class="pre">api.run()</span></code> is all you need:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
|
||
<span class="n">api</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This starts a <a class="reference external" href="https://www.uvicorn.org/">uvicorn</a> server on
|
||
<code class="docutils literal notranslate"><span class="pre">127.0.0.1:5042</span></code>. Uvicorn is a lightning-fast ASGI server built on
|
||
<a class="reference external" href="https://uvloop.readthedocs.io/">uvloop</a> — it handles thousands of
|
||
concurrent connections efficiently and protects against slowloris attacks,
|
||
making a reverse proxy like nginx optional for many deployments.</p>
|
||
<p>Responder can also run the current app with Granian when the optional server
|
||
extra is installed:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ uv pip install 'responder[orjson,server]'
|
||
|
||
if __name__ == "__main__":
|
||
api.run(server="granian")
|
||
</pre></div>
|
||
</div>
|
||
<p>If the extra is missing, Responder raises an error with the install command.
|
||
This embedded Granian path is single-process; use Granian directly for
|
||
multi-worker production deployments.</p>
|
||
</section>
|
||
<section id="docker">
|
||
<h2>Docker<a class="headerlink" href="#docker" title="Link to this heading">¶</a></h2>
|
||
<p>Docker is the most common way to package and deploy web applications.
|
||
Here’s a minimal Dockerfile:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">FROM</span> <span class="n">python</span><span class="p">:</span><span class="mf">3.13</span><span class="o">-</span><span class="n">slim</span>
|
||
<span class="n">WORKDIR</span> <span class="o">/</span><span class="n">app</span>
|
||
<span class="n">COPY</span> <span class="o">--</span><span class="n">from</span><span class="o">=</span><span class="n">ghcr</span><span class="o">.</span><span class="n">io</span><span class="o">/</span><span class="n">astral</span><span class="o">-</span><span class="n">sh</span><span class="o">/</span><span class="n">uv</span><span class="p">:</span><span class="n">latest</span> <span class="o">/</span><span class="n">uv</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">local</span><span class="o">/</span><span class="nb">bin</span><span class="o">/</span><span class="n">uv</span>
|
||
<span class="n">COPY</span> <span class="o">.</span> <span class="o">.</span>
|
||
<span class="n">RUN</span> <span class="n">uv</span> <span class="n">pip</span> <span class="n">install</span> <span class="o">--</span><span class="n">system</span> <span class="s1">'responder[orjson]'</span>
|
||
<span class="n">ENV</span> <span class="n">PORT</span><span class="o">=</span><span class="mi">80</span>
|
||
<span class="n">EXPOSE</span> <span class="mi">80</span>
|
||
<span class="n">CMD</span> <span class="p">[</span><span class="s2">"python"</span><span class="p">,</span> <span class="s2">"api.py"</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Build and run:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ docker build -t myapi .
|
||
$ docker run -p 8000:80 myapi
|
||
</pre></div>
|
||
</div>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">python:3.13-slim</span></code> image is about 150MB — small enough for fast
|
||
deploys but includes everything you need. Using <code class="docutils literal notranslate"><span class="pre">uv</span></code> for installs
|
||
is significantly faster than pip. For even smaller images, you can use
|
||
<code class="docutils literal notranslate"><span class="pre">python:3.13-alpine</span></code>, though some packages may need extra build
|
||
dependencies.</p>
|
||
</section>
|
||
<section id="cloud-platforms">
|
||
<h2>Cloud Platforms<a class="headerlink" href="#cloud-platforms" title="Link to this heading">¶</a></h2>
|
||
<p>Responder automatically honors the <code class="docutils literal notranslate"><span class="pre">PORT</span></code> environment variable. When
|
||
<code class="docutils literal notranslate"><span class="pre">PORT</span></code> is set and you do not pass an explicit <code class="docutils literal notranslate"><span class="pre">port=</span></code>, the server binds
|
||
to <code class="docutils literal notranslate"><span class="pre">0.0.0.0</span></code> on that port — this is the convention that virtually every
|
||
cloud platform uses.</p>
|
||
<p>This means zero configuration on:</p>
|
||
<ul class="simple">
|
||
<li><p><strong>Fly.io</strong> — <code class="docutils literal notranslate"><span class="pre">fly</span> <span class="pre">launch</span></code> and you’re done</p></li>
|
||
<li><p><strong>Railway</strong> — push your code, Railway sets <code class="docutils literal notranslate"><span class="pre">PORT</span></code></p></li>
|
||
<li><p><strong>Render</strong> — set start command to <code class="docutils literal notranslate"><span class="pre">python</span> <span class="pre">api.py</span></code></p></li>
|
||
<li><p><strong>Google Cloud Run</strong> — containerize and deploy</p></li>
|
||
<li><p><strong>Azure Container Apps</strong> — same pattern</p></li>
|
||
<li><p><strong>AWS App Runner</strong> — and here too</p></li>
|
||
</ul>
|
||
<p>The pattern is always the same: deploy your code, set the start command
|
||
to <code class="docutils literal notranslate"><span class="pre">python</span> <span class="pre">api.py</span></code>, and the platform handles the rest.</p>
|
||
</section>
|
||
<section id="health-check-endpoint">
|
||
<h2>Health Check Endpoint<a class="headerlink" href="#health-check-endpoint" title="Link to this heading">¶</a></h2>
|
||
<p>Every production deployment needs a health check — a lightweight endpoint
|
||
that monitoring tools, load balancers, and orchestrators can poll to verify
|
||
your service is running:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">"/health"</span><span class="p">,</span> <span class="n">include_in_schema</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">health</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
|
||
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"status"</span><span class="p">:</span> <span class="s2">"healthy"</span><span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Keep it simple. Don’t query the database or do expensive work — the health
|
||
check should return instantly. Cloud platforms, Docker, and Kubernetes all
|
||
look for an HTTP 200 to confirm your service is alive.</p>
|
||
<p>Responder generates your <a class="reference internal" href="tour.html"><span class="doc">OpenAPI schema</span></a> from route signatures, so
|
||
every route is documented automatically. <code class="docutils literal notranslate"><span class="pre">include_in_schema=False</span></code> keeps this
|
||
internal endpoint out of the public spec.</p>
|
||
<p>For Docker, add a <code class="docutils literal notranslate"><span class="pre">HEALTHCHECK</span></code> instruction:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">HEALTHCHECK</span> <span class="o">--</span><span class="n">interval</span><span class="o">=</span><span class="mi">30</span><span class="n">s</span> <span class="o">--</span><span class="n">timeout</span><span class="o">=</span><span class="mi">3</span><span class="n">s</span> \
|
||
<span class="n">CMD</span> <span class="n">curl</span> <span class="o">-</span><span class="n">f</span> <span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">localhost</span><span class="o">/</span><span class="n">health</span> <span class="o">||</span> <span class="n">exit</span> <span class="mi">1</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="uvicorn-directly">
|
||
<h2>Uvicorn Directly<a class="headerlink" href="#uvicorn-directly" title="Link to this heading">¶</a></h2>
|
||
<p>For production deployments where you want more control, bypass
|
||
<code class="docutils literal notranslate"><span class="pre">api.run()</span></code> and use uvicorn directly:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ uvicorn api:api --host 0.0.0.0 --port 8000 --workers 4
|
||
</pre></div>
|
||
</div>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">--workers</span></code> flag spawns multiple processes, each handling requests
|
||
independently. A good starting point is 2-4 workers per CPU core.</p>
|
||
<div class="admonition note">
|
||
<p class="admonition-title">Note</p>
|
||
<p>Each worker is a separate process. With the default <code class="docutils literal notranslate"><span class="pre">sessions="auto"</span></code> and
|
||
no signing key set, every worker mints its <strong>own</strong> random key, so signed
|
||
session cookies won’t validate across workers and load-balanced users get
|
||
logged out. Before running more than one worker or instance, set a stable
|
||
<code class="docutils literal notranslate"><span class="pre">RESPONDER_SECRET_KEY</span></code> (or <code class="docutils literal notranslate"><span class="pre">API(secret_key=...)</span></code>). For sessions that
|
||
survive across separate machines, store them server-side with a shared
|
||
backend such as <code class="docutils literal notranslate"><span class="pre">AsyncRedisSessionBackend</span></code>. See the
|
||
<a class="reference internal" href="guide-config.html"><span class="doc">configuration guide</span></a>.</p>
|
||
</div>
|
||
<p>Uvicorn supports many options — SSL certificates, access logging, graceful
|
||
shutdown timeouts, and more. See the
|
||
<a class="reference external" href="https://www.uvicorn.org/deployment/">uvicorn documentation</a> for details.</p>
|
||
<p>For platforms like Heroku or Railway that use a <code class="docutils literal notranslate"><span class="pre">Procfile</span></code>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>web: uvicorn api:api --host 0.0.0.0 --port $PORT --workers 4
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="granian">
|
||
<h2>Granian<a class="headerlink" href="#granian" title="Link to this heading">¶</a></h2>
|
||
<p><a class="reference external" href="https://github.com/emmett-framework/granian">Granian</a> is a Rust-based HTTP
|
||
server that runs ASGI, WSGI, and RSGI apps from a single dependency. It’s a
|
||
strong production peer to uvicorn — native HTTP/2, WebSockets enabled by
|
||
default, and no separate worker package to install.</p>
|
||
<p>Install it:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ uv pip install 'responder[orjson,server]'
|
||
</pre></div>
|
||
</div>
|
||
<p>Responder apps are ASGI, so run them with the <code class="docutils literal notranslate"><span class="pre">asgi</span></code> interface:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ granian --interface asgi api:api
|
||
</pre></div>
|
||
</div>
|
||
<p>The familiar host, port, and worker flags all apply:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ granian --interface asgi --host 0.0.0.0 --port 8000 --workers 4 api:api
|
||
</pre></div>
|
||
</div>
|
||
<p>To serve HTTP/2 in production, add <code class="docutils literal notranslate"><span class="pre">--http</span> <span class="pre">2</span></code>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ granian --interface asgi --host 0.0.0.0 --port 8000 --workers 4 --http 2 api:api
|
||
</pre></div>
|
||
</div>
|
||
<p>For a <code class="docutils literal notranslate"><span class="pre">Procfile</span></code>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>web: granian --interface asgi --host 0.0.0.0 --port $PORT --workers 4 api:api
|
||
</pre></div>
|
||
</div>
|
||
<p>Like uvicorn’s <code class="docutils literal notranslate"><span class="pre">--workers</span></code>, every Granian worker is a separate process, so the
|
||
stable-secret-key note above applies here too. <code class="docutils literal notranslate"><span class="pre">api.run(server="granian")</span></code>
|
||
uses Granian’s embedded server for the current app object; for workers, reload,
|
||
or process supervision, use the Granian CLI shown above.</p>
|
||
</section>
|
||
<section id="docker-compose">
|
||
<h2>Docker Compose<a class="headerlink" href="#docker-compose" title="Link to this heading">¶</a></h2>
|
||
<p>For local development with databases and other services, Docker Compose
|
||
ties everything together:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># docker-compose.yml</span>
|
||
<span class="n">services</span><span class="p">:</span>
|
||
<span class="n">api</span><span class="p">:</span>
|
||
<span class="n">build</span><span class="p">:</span> <span class="o">.</span>
|
||
<span class="n">ports</span><span class="p">:</span>
|
||
<span class="o">-</span> <span class="s2">"5042:80"</span>
|
||
<span class="n">environment</span><span class="p">:</span>
|
||
<span class="o">-</span> <span class="n">PORT</span><span class="o">=</span><span class="mi">80</span>
|
||
<span class="o">-</span> <span class="n">DATABASE_URL</span><span class="o">=</span><span class="n">postgresql</span><span class="o">+</span><span class="n">asyncpg</span><span class="p">:</span><span class="o">//</span><span class="n">user</span><span class="p">:</span><span class="k">pass</span><span class="nd">@db</span><span class="o">/</span><span class="n">myapp</span>
|
||
<span class="o">-</span> <span class="n">RESPONDER_SECRET_KEY</span><span class="o">=</span><span class="n">dev</span><span class="o">-</span><span class="n">only</span><span class="o">-</span><span class="ow">not</span><span class="o">-</span><span class="k">for</span><span class="o">-</span><span class="n">production</span><span class="o">-</span><span class="mi">32</span><span class="n">chars</span>
|
||
<span class="n">depends_on</span><span class="p">:</span>
|
||
<span class="o">-</span> <span class="n">db</span>
|
||
|
||
<span class="n">db</span><span class="p">:</span>
|
||
<span class="n">image</span><span class="p">:</span> <span class="n">docker</span><span class="o">.</span><span class="n">io</span><span class="o">/</span><span class="n">postgres</span><span class="p">:</span><span class="mi">16</span><span class="o">-</span><span class="n">alpine</span>
|
||
<span class="n">environment</span><span class="p">:</span>
|
||
<span class="n">POSTGRES_USER</span><span class="p">:</span> <span class="n">user</span>
|
||
<span class="n">POSTGRES_PASSWORD</span><span class="p">:</span> <span class="k">pass</span>
|
||
<span class="n">POSTGRES_DB</span><span class="p">:</span> <span class="n">myapp</span>
|
||
<span class="n">volumes</span><span class="p">:</span>
|
||
<span class="o">-</span> <span class="n">pgdata</span><span class="p">:</span><span class="o">/</span><span class="n">var</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">postgresql</span><span class="o">/</span><span class="n">data</span>
|
||
|
||
<span class="n">volumes</span><span class="p">:</span>
|
||
<span class="n">pgdata</span><span class="p">:</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Run with <code class="docutils literal notranslate"><span class="pre">docker</span> <span class="pre">compose</span> <span class="pre">up</span></code>. The API waits for <code class="docutils literal notranslate"><span class="pre">db</span></code> to start, then
|
||
connects using the <code class="docutils literal notranslate"><span class="pre">DATABASE_URL</span></code> environment variable.</p>
|
||
</section>
|
||
<section id="reverse-proxy">
|
||
<h2>Reverse Proxy<a class="headerlink" href="#reverse-proxy" title="Link to this heading">¶</a></h2>
|
||
<p>For high-traffic production deployments, you may want a reverse proxy like
|
||
<a class="reference external" href="https://nginx.org/">nginx</a> or <a class="reference external" href="https://caddyserver.com/">Caddy</a> in
|
||
front of your application for:</p>
|
||
<ul class="simple">
|
||
<li><p><strong>SSL/TLS termination</strong> — let the proxy handle HTTPS certificates</p></li>
|
||
<li><p><strong>Load balancing</strong> — distribute traffic across multiple app instances</p></li>
|
||
<li><p><strong>Static asset serving</strong> — offload static files to the proxy</p></li>
|
||
<li><p><strong>Rate limiting</strong> — at the infrastructure level</p></li>
|
||
</ul>
|
||
<p>A minimal Caddy config that handles HTTPS automatically:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Caddyfile</span>
|
||
<span class="n">example</span><span class="o">.</span><span class="n">com</span> <span class="p">{</span>
|
||
<span class="n">reverse_proxy</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">5042</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Behind a proxy, tell Responder to trust its forwarding headers:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">api</span> <span class="o">=</span> <span class="n">responder</span><span class="o">.</span><span class="n">API</span><span class="p">(</span><span class="n">trust_proxy_headers</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Responder then honors RFC 7239 <code class="docutils literal notranslate"><span class="pre">Forwarded</span></code> (or <code class="docutils literal notranslate"><span class="pre">X-Forwarded-Proto</span></code> /
|
||
<code class="docutils literal notranslate"><span class="pre">X-Forwarded-Host</span></code> / <code class="docutils literal notranslate"><span class="pre">X-Forwarded-For</span></code>) and rewrites the request’s
|
||
scheme, host, and client address to what the original client sent — so
|
||
HTTPS detection, redirects, URL building, host validation, and logged or
|
||
rate-limited client IPs are all correct. Only enable it when every request
|
||
reaches Responder through a proxy you control that overwrites those headers;
|
||
otherwise clients can spoof them.</p>
|
||
<p>Behind a TLS-terminating proxy this is exactly right: in production
|
||
(<code class="docutils literal notranslate"><span class="pre">debug=False</span></code>) Responder marks the session cookie <code class="docutils literal notranslate"><span class="pre">Secure</span></code> by default, so
|
||
it only travels over HTTPS — no action needed. Only pass
|
||
<code class="docutils literal notranslate"><span class="pre">session_https_only=False</span></code> if you genuinely serve plain HTTP. (Browsers reject
|
||
<code class="docutils literal notranslate"><span class="pre">SameSite=None</span></code> without a Secure cookie, so Responder rejects that combination
|
||
too.)</p>
|
||
<p>That said, uvicorn and Granian are both production-ready on their own. Many
|
||
applications run the ASGI server directly without a reverse proxy and do just
|
||
fine.</p>
|
||
</section>
|
||
<section id="production-checklist">
|
||
<h2>Production Checklist<a class="headerlink" href="#production-checklist" title="Link to this heading">¶</a></h2>
|
||
<p>Before going live:</p>
|
||
<ul class="simple">
|
||
<li><p><strong>Set a stable secret key</strong> — pass <code class="docutils literal notranslate"><span class="pre">API(secret_key=...)</span></code> or set
|
||
<code class="docutils literal notranslate"><span class="pre">RESPONDER_SECRET_KEY</span></code> (16+ chars; generate one with
|
||
<code class="docutils literal notranslate"><span class="pre">python</span> <span class="pre">-c</span> <span class="pre">"import</span> <span class="pre">secrets;</span> <span class="pre">print(secrets.token_urlsafe(32))"</span></code>). It must be
|
||
stable across workers and restarts, or signed sessions stop validating. If
|
||
your service is stateless and never touches <code class="docutils literal notranslate"><span class="pre">req.session</span></code>, pass
|
||
<code class="docutils literal notranslate"><span class="pre">API(sessions=False)</span></code> instead to skip sessions entirely. See the
|
||
<a class="reference internal" href="guide-config.html"><span class="doc">configuration guide</span></a>.</p></li>
|
||
<li><p><strong>Disable debug mode</strong> — it’s off by default; never set <code class="docutils literal notranslate"><span class="pre">debug=True</span></code> in production</p></li>
|
||
<li><p><strong>Set allowed hosts</strong> — <code class="docutils literal notranslate"><span class="pre">allowed_hosts=[...]</span></code>, restricted to your domains</p></li>
|
||
<li><p><strong>Behind a proxy? Trust its headers</strong> — <code class="docutils literal notranslate"><span class="pre">trust_proxy_headers=True</span></code> so
|
||
HTTPS detection, redirects, and client IPs reflect the original request
|
||
(only when <em>every</em> request comes through your proxy — see <a class="reference internal" href="#reverse-proxy">Reverse Proxy</a>)</p></li>
|
||
<li><p><strong>Serving browser forms? Enable CSRF protection</strong> — <code class="docutils literal notranslate"><span class="pre">csrf=True</span></code> pairs
|
||
with the default-on cookie sessions; exempt webhook routes with
|
||
<code class="docutils literal notranslate"><span class="pre">csrf=False</span></code>. Token-only APIs (no cookies) don’t need it.</p></li>
|
||
<li><p><strong>Check the request-body cap fits</strong> — bodies over <code class="docutils literal notranslate"><span class="pre">max_request_size</span></code>
|
||
(100 MiB by default) get a <code class="docutils literal notranslate"><span class="pre">413</span></code>; raise it for large uploads (multipart
|
||
streams to disk, so a bigger cap doesn’t mean more memory)</p></li>
|
||
<li><p><strong>Use multiple workers</strong> — <code class="docutils literal notranslate"><span class="pre">--workers</span> <span class="pre">4</span></code> or more, depending on CPU cores
|
||
(set a stable secret key first — see above)</p></li>
|
||
<li><p><strong>Add a health check</strong> — <code class="docutils literal notranslate"><span class="pre">/health</span></code> endpoint for monitoring</p></li>
|
||
<li><p><strong>Enable HTTPS</strong> — via your proxy, cloud platform, or your ASGI server’s
|
||
<code class="docutils literal notranslate"><span class="pre">--ssl-*</span></code> flags; the session cookie is then <code class="docutils literal notranslate"><span class="pre">Secure</span></code> automatically</p></li>
|
||
<li><p><strong>Set up logging</strong> — your ASGI server logs requests by default; pipe them to your log aggregator</p></li>
|
||
<li><p><strong>Pin your dependencies</strong> — use a lock file or pinned requirements for reproducible deploys</p></li>
|
||
</ul>
|
||
</section>
|
||
</section>
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
<div class="sphinxsidebar" role="navigation" aria-label="Main">
|
||
<div class="sphinxsidebarwrapper"><p class="logo">
|
||
<a href="index.html">
|
||
<img class="logo" src="_static/responder.png" />
|
||
</a>
|
||
</p>
|
||
<p>
|
||
<strong>Responder</strong> — a familiar HTTP service framework for Python.
|
||
<br />
|
||
<small>v9.2.0</small>
|
||
</p>
|
||
<h3>Useful Links</h3>
|
||
<ul>
|
||
<li><a href="https://github.com/kennethreitz/responder">Responder @ GitHub</a></li>
|
||
<li><a href="https://pypi.org/project/responder/">Responder @ PyPI</a></li>
|
||
<li><a href="https://github.com/kennethreitz/responder/issues">Issue Tracker</a></li>
|
||
</ul><h3>Navigation</h3>
|
||
<p class="caption" role="heading"><span class="caption-text">User Guide</span></p>
|
||
<ul class="current">
|
||
<li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quick Start</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="tour.html">Feature Tour</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="examples.html">Examples</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="routers.html">Composing Apps with Routers</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="guide-config.html">Configuration</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="clientgen.html">Generated Clients</a></li>
|
||
<li class="toctree-l1 current"><a class="current reference internal" href="#">Deployment</a><ul>
|
||
<li class="toctree-l2"><a class="reference internal" href="#running-locally">Running Locally</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="#docker">Docker</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="#cloud-platforms">Cloud Platforms</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="#health-check-endpoint">Health Check Endpoint</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="#uvicorn-directly">Uvicorn Directly</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="#granian">Granian</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="#docker-compose">Docker Compose</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="#reverse-proxy">Reverse Proxy</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="#production-checklist">Production Checklist</a></li>
|
||
</ul>
|
||
</li>
|
||
<li class="toctree-l1"><a class="reference internal" href="testing.html">Testing</a></li>
|
||
</ul>
|
||
<p class="caption" role="heading"><span class="caption-text">Tutorials</span></p>
|
||
<ul>
|
||
<li class="toctree-l1"><a class="reference internal" href="tutorial-rest.html">Building a REST API</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="tutorial-sqlalchemy.html">Using SQLAlchemy</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="tutorial-auth.html">Authentication</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="tutorial-websockets.html">WebSocket Tutorial</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="tutorial-middleware.html">Writing Middleware</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="tutorial-flask.html">Migrating from Flask</a></li>
|
||
</ul>
|
||
<p class="caption" role="heading"><span class="caption-text">Reference</span></p>
|
||
<ul>
|
||
<li class="toctree-l1"><a class="reference internal" href="api.html">API Reference</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="cli.html">Command Line Interface</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="runtime-contracts.html">Runtime Contracts</a></li>
|
||
</ul>
|
||
<p class="caption" role="heading"><span class="caption-text">Project</span></p>
|
||
<ul>
|
||
<li class="toctree-l1"><a class="reference internal" href="changes.html">Changelog</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="migration-v9.html">Migrating to v9</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="migration-v8.html">Migrating to v8</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="migration-v7.html">Migrating to v7</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="migration-v6.html">Migrating to v6</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="migration-v5.html">Migrating to v5</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="sandbox.html">Sandbox</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="backlog.html">Backlog</a></li>
|
||
</ul>
|
||
|
||
<div class="relations">
|
||
<h3>Related Topics</h3>
|
||
<ul>
|
||
<li><a href="index.html">Documentation overview</a><ul>
|
||
<li>Previous: <a href="clientgen.html" title="previous chapter">Generated Clients</a></li>
|
||
<li>Next: <a href="testing.html" title="next chapter">Testing</a></li>
|
||
</ul></li>
|
||
</ul>
|
||
</div>
|
||
<search id="searchbox" style="display: none" role="search">
|
||
<h3 id="searchlabel">Quick search</h3>
|
||
<div class="searchformwrapper">
|
||
<form class="search" action="search.html" method="get">
|
||
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
|
||
<input type="submit" value="Go" />
|
||
</form>
|
||
</div>
|
||
</search>
|
||
<script>document.getElementById('searchbox').style.display = "block"</script>
|
||
</div>
|
||
</div>
|
||
<div class="clearer"></div>
|
||
</div>
|
||
<div class="footer">
|
||
©2018-2026, Kenneth Reitz.
|
||
|
||
|
|
||
<a href="_sources/deployment.rst.txt"
|
||
rel="nofollow">Page source</a>
|
||
</div>
|
||
|
||
|
||
<a href="https://github.com/kennethreitz/responder" class="github">
|
||
<img src="_static/forkme_right_darkblue.png" alt="Fork me on GitHub" class="github"/>
|
||
</a>
|
||
|
||
|
||
|
||
</body>
|
||
</html> |