Files
responder/guide-config.html
2026-07-14 04:14:56 +00:00

405 lines
35 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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>Configuration &#8212; 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="Generated Clients" href="clientgen.html" />
<link rel="prev" title="Composing Apps with Routers" href="routers.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="configuration">
<h1>Configuration<a class="headerlink" href="#configuration" title="Link to this heading"></a></h1>
<p>Every application needs different settings for different environments —
debug mode in development, real secrets in production, different database
URLs for testing. This guide covers how to manage configuration cleanly.</p>
<section id="environment-variables">
<h2>Environment Variables<a class="headerlink" href="#environment-variables" title="Link to this heading"></a></h2>
<p>The simplest and most universal approach. Environment variables work
everywhere — locally, in Docker, on cloud platforms — and keep secrets
out of your source code:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">os</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">responder</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="c1"># RESPONDER_SECRET_KEY is picked up from the environment automatically</span>
<span class="n">debug</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">&quot;DEBUG&quot;</span><span class="p">,</span> <span class="s2">&quot;false&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="o">==</span> <span class="s2">&quot;true&quot;</span><span class="p">,</span>
<span class="n">cors</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">&quot;CORS_ENABLED&quot;</span><span class="p">,</span> <span class="s2">&quot;false&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="o">==</span> <span class="s2">&quot;true&quot;</span><span class="p">,</span>
<span class="p">)</span>
</pre></div>
</div>
<p>Some variables Responder reads automatically:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">PORT</span></code> — when set, <code class="docutils literal notranslate"><span class="pre">api.run()</span></code> binds to <code class="docutils literal notranslate"><span class="pre">0.0.0.0</span></code> on this port
unless an explicit <code class="docutils literal notranslate"><span class="pre">port=</span></code> is also passed</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">RESPONDER_SECRET_KEY</span></code> — the signing key for sessions, used when you
don’t pass <code class="docutils literal notranslate"><span class="pre">secret_key=</span></code> explicitly (see <a class="reference internal" href="#secret-key">Secret Key</a> below)</p></li>
</ul>
<p>Set variables in your shell:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ export RESPONDER_SECRET_KEY=&quot;your-secret-here&quot;
$ export DEBUG=true
$ python app.py
</pre></div>
</div>
<p>Or in a <code class="docutils literal notranslate"><span class="pre">.env</span></code> file (don’t commit this to git):</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">RESPONDER_SECRET_KEY</span><span class="o">=</span><span class="n">your</span><span class="o">-</span><span class="n">secret</span><span class="o">-</span><span class="n">here</span>
<span class="n">DEBUG</span><span class="o">=</span><span class="n">true</span>
</pre></div>
</div>
</section>
<section id="using-env-files">
<h2>Using .env Files<a class="headerlink" href="#using-env-files" title="Link to this heading"></a></h2>
<p>For local development, a <code class="docutils literal notranslate"><span class="pre">.env</span></code> file is convenient. Install
<code class="docutils literal notranslate"><span class="pre">python-dotenv</span></code> and load it at the top of your app:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ uv pip install python-dotenv
</pre></div>
</div>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">dotenv</span><span class="w"> </span><span class="kn">import</span> <span class="n">load_dotenv</span>
<span class="n">load_dotenv</span><span class="p">()</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">responder</span>
<span class="c1"># Picks up RESPONDER_SECRET_KEY (and friends) from the loaded .env</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>
</pre></div>
</div>
<p>Add <code class="docutils literal notranslate"><span class="pre">.env</span></code> to your <code class="docutils literal notranslate"><span class="pre">.gitignore</span></code> — never commit secrets.</p>
</section>
<section id="configuration-class-pattern">
<h2>Configuration Class Pattern<a class="headerlink" href="#configuration-class-pattern" title="Link to this heading"></a></h2>
<p>For larger applications, a configuration class keeps things organized:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">os</span>
<span class="k">class</span><span class="w"> </span><span class="nc">Config</span><span class="p">:</span>
<span class="n">SECRET_KEY</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;RESPONDER_SECRET_KEY&quot;</span><span class="p">)</span> <span class="c1"># None in dev → auto-generated</span>
<span class="n">DEBUG</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;DEBUG&quot;</span><span class="p">,</span> <span class="s2">&quot;false&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="o">==</span> <span class="s2">&quot;true&quot;</span>
<span class="n">DATABASE_URL</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;DATABASE_URL&quot;</span><span class="p">,</span> <span class="s2">&quot;sqlite:///dev.db&quot;</span><span class="p">)</span>
<span class="n">CORS_ORIGINS</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;CORS_ORIGINS&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;,&quot;</span><span class="p">)</span>
<span class="n">config</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</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">debug</span><span class="o">=</span><span class="n">config</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">,</span>
<span class="n">secret_key</span><span class="o">=</span><span class="n">config</span><span class="o">.</span><span class="n">SECRET_KEY</span><span class="p">,</span>
<span class="n">cors</span><span class="o">=</span><span class="nb">bool</span><span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">CORS_ORIGINS</span><span class="p">[</span><span class="mi">0</span><span class="p">]),</span>
<span class="n">cors_params</span><span class="o">=</span><span class="p">{</span><span class="s2">&quot;allow_origins&quot;</span><span class="p">:</span> <span class="n">config</span><span class="o">.</span><span class="n">CORS_ORIGINS</span><span class="p">},</span>
<span class="p">)</span>
</pre></div>
</div>
<p>This makes it easy to see all your settings in one place.</p>
</section>
<section id="secret-key">
<h2>Secret Key<a class="headerlink" href="#secret-key" title="Link to this heading"></a></h2>
<p>The <code class="docutils literal notranslate"><span class="pre">secret_key</span></code> signs cookie-based session data. If someone knows your
secret key, they can forge sessions and impersonate any user, so it must
be a real random value and stay out of source control.</p>
<p>Responder resolves the key in this order:</p>
<ol class="arabic simple">
<li><p>the explicit <code class="docutils literal notranslate"><span class="pre">secret_key=</span></code> argument</p></li>
<li><p>the <code class="docutils literal notranslate"><span class="pre">RESPONDER_SECRET_KEY</span></code> environment variable</p></li>
<li><p>otherwise, under the default <code class="docutils literal notranslate"><span class="pre">sessions=&quot;auto&quot;</span></code>, a random key minted
per process at startup (with a loud warning)</p></li>
</ol>
<p>Generate one with:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ python -c &quot;import secrets; print(secrets.token_urlsafe(32))&quot;
</pre></div>
</div>
<p>Then supply it via the environment — no code required:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ export RESPONDER_SECRET_KEY=&quot;paste-the-generated-key-here&quot;
</pre></div>
</div>
<p>or pass it explicitly:</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">secret_key</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s2">&quot;RESPONDER_SECRET_KEY&quot;</span><span class="p">])</span>
</pre></div>
</div>
<p>A few rules worth knowing:</p>
<ul class="simple">
<li><p><strong>Set a stable key in production.</strong> The auto-minted key (step 3) is
different in every worker and is regenerated on restart, so signed
session cookies stop validating across processes — users get logged
out. It’s fine for a single dev process, but any multi-worker or
multi-instance deploy needs a fixed <code class="docutils literal notranslate"><span class="pre">RESPONDER_SECRET_KEY</span></code>.</p></li>
<li><p><strong>The old public default is rejected.</strong> <code class="docutils literal notranslate"><span class="pre">secret_key=&quot;NOTASECRET&quot;</span></code>
now raises <code class="docutils literal notranslate"><span class="pre">SessionConfigError</span></code> (a <code class="docutils literal notranslate"><span class="pre">ValueError</span></code> subclass from
<code class="docutils literal notranslate"><span class="pre">responder.ext.sessions</span></code>).</p></li>
<li><p><strong>Use at least 16 characters.</strong> Shorter keys are accepted but warn.</p></li>
<li><p><strong>Rotate it</strong> if it’s ever compromised — this invalidates all existing
sessions.</p></li>
</ul>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>A server-side session backend stores data on the server and keeps only
an opaque id in the cookie, so it doesn’t sign anything — <code class="docutils literal notranslate"><span class="pre">secret_key</span></code>
is irrelevant in that mode. See <a class="reference internal" href="#sessions">Sessions</a> below.</p>
</div>
</section>
<section id="sessions">
<span id="guide-config-sessions"></span><h2>Sessions<a class="headerlink" href="#sessions" title="Link to this heading"></a></h2>
<p>Sessions are enabled by default and secure out of the box. The
<code class="docutils literal notranslate"><span class="pre">sessions=</span></code> knob controls them:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;auto&quot;</span></code> (default) — cookie sessions on; auto-mints an ephemeral key
if none is set (see <a class="reference internal" href="#secret-key">Secret Key</a>).</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">True</span></code> — strict mode: requires a real key via <code class="docutils literal notranslate"><span class="pre">secret_key=</span></code> or
<code class="docutils literal notranslate"><span class="pre">RESPONDER_SECRET_KEY</span></code>, and raises <code class="docutils literal notranslate"><span class="pre">SessionConfigError</span></code> if none is
found.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">False</span></code> — disables session middleware entirely. Accessing
<code class="docutils literal notranslate"><span class="pre">req.session</span></code> / <code class="docutils literal notranslate"><span class="pre">resp.session</span></code> then raises <code class="docutils literal notranslate"><span class="pre">RuntimeError</span></code>. Use
this for stateless APIs to silence the auto-key startup warning.</p></li>
</ul>
<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">sessions</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="c1"># stateless service, no sessions</span>
</pre></div>
</div>
<p>The cookie and lifetime are tunable:</p>
<ul>
<li><p><code class="docutils literal notranslate"><span class="pre">session_https_only</span></code> (default <code class="docutils literal notranslate"><span class="pre">None</span></code>) — marks the cookie <code class="docutils literal notranslate"><span class="pre">Secure</span></code>
in production (<code class="docutils literal notranslate"><span class="pre">debug=False</span></code>) and not under <code class="docutils literal notranslate"><span class="pre">debug=True</span></code>. Behind a
TLS-terminating proxy this needs no action; pass <code class="docutils literal notranslate"><span class="pre">False</span></code> only if you
genuinely serve plain HTTP.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">session_same_site</span></code> (default <code class="docutils literal notranslate"><span class="pre">&quot;lax&quot;</span></code>) — <code class="docutils literal notranslate"><span class="pre">&quot;strict&quot;</span></code>, <code class="docutils literal notranslate"><span class="pre">&quot;lax&quot;</span></code>, or
<code class="docutils literal notranslate"><span class="pre">&quot;none&quot;</span></code>. <code class="docutils literal notranslate"><span class="pre">&quot;none&quot;</span></code> without a <code class="docutils literal notranslate"><span class="pre">Secure</span></code> cookie raises <code class="docutils literal notranslate"><span class="pre">ValueError</span></code>.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">session_cookie</span></code> (default <code class="docutils literal notranslate"><span class="pre">None</span></code> → <code class="docutils literal notranslate"><span class="pre">&quot;session&quot;</span></code>) — the cookie name.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">session_max_age</span></code> (default <code class="docutils literal notranslate"><span class="pre">1209600</span></code>, i.e. 14 days) — lifetime in
seconds.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">session_backend</span></code> (default <code class="docutils literal notranslate"><span class="pre">None</span></code>) — by default, session data is
stored in a signed cookie. Pass a backend to keep data server-side
instead, leaving only an opaque id in the cookie:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">responder.ext.sessions</span><span class="w"> </span><span class="kn">import</span> <span class="n">RedisSessionBackend</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">session_backend</span><span class="o">=</span><span class="n">RedisSessionBackend</span><span class="p">(</span><span class="n">url</span><span class="o">=</span><span class="s2">&quot;redis://localhost:6379/0&quot;</span><span class="p">),</span>
<span class="n">session_max_age</span><span class="o">=</span><span class="mi">3600</span><span class="p">,</span> <span class="c1"># 1 hour</span>
<span class="p">)</span>
</pre></div>
</div>
<p>Built-in backends are <code class="docutils literal notranslate"><span class="pre">MemorySessionBackend</span></code> (single-process, dev
only), <code class="docutils literal notranslate"><span class="pre">RedisSessionBackend</span></code>, and <code class="docutils literal notranslate"><span class="pre">AsyncRedisSessionBackend</span></code>
(preferred for async apps). With a backend, <code class="docutils literal notranslate"><span class="pre">secret_key</span></code> is unused.
Pairing <code class="docutils literal notranslate"><span class="pre">sessions=False</span></code> with a backend raises <code class="docutils literal notranslate"><span class="pre">ValueError</span></code>.</p>
</li>
</ul>
<p>For reading and writing session data in handlers — and rotating the
session id on login with <code class="docutils literal notranslate"><span class="pre">regenerate_session</span></code> — see
<a class="reference internal" href="tutorial-auth.html"><span class="doc">Authentication</span></a>.</p>
</section>
<section id="debug-mode">
<h2>Debug Mode<a class="headerlink" href="#debug-mode" title="Link to this heading"></a></h2>
<p>Debug mode controls error page behavior:</p>
<ul class="simple">
<li><p><strong>On</strong> (<code class="docutils literal notranslate"><span class="pre">debug=True</span></code>): detailed error pages with tracebacks. Never
use this in production — it exposes your source code.</p></li>
<li><p><strong>Off</strong> (<code class="docutils literal notranslate"><span class="pre">debug=False</span></code>): generic error pages. This is the default.</p></li>
</ul>
<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">debug</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="c1"># development only</span>
</pre></div>
</div>
<p>A common pattern is to read it from the environment:</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">debug</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">&quot;DEBUG&quot;</span><span class="p">)</span> <span class="o">==</span> <span class="s2">&quot;true&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>Debug also relaxes session cookie security: with the default
<code class="docutils literal notranslate"><span class="pre">session_https_only=None</span></code>, session cookies are marked <code class="docutils literal notranslate"><span class="pre">Secure</span></code> in
production but not under <code class="docutils literal notranslate"><span class="pre">debug=True</span></code> (so they round-trip over plain
<code class="docutils literal notranslate"><span class="pre">http://</span></code> while you develop). See <a class="reference internal" href="#sessions">Sessions</a>.</p>
</section>
<section id="allowed-hosts">
<h2>Allowed Hosts<a class="headerlink" href="#allowed-hosts" title="Link to this heading"></a></h2>
<p>In production, always set <code class="docutils literal notranslate"><span class="pre">allowed_hosts</span></code> to prevent Host header
attacks. This should match the domain names your application serves:</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">allowed_hosts</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;example.com&quot;</span><span class="p">,</span> <span class="s2">&quot;www.example.com&quot;</span><span class="p">],</span>
<span class="p">)</span>
</pre></div>
</div>
<p>In development, you can use <code class="docutils literal notranslate"><span class="pre">[&quot;*&quot;]</span></code> (the default) or specific local
addresses:</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">allowed_hosts</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;localhost&quot;</span><span class="p">,</span> <span class="s2">&quot;127.0.0.1&quot;</span><span class="p">])</span>
</pre></div>
</div>
</section>
<section id="putting-it-all-together">
<h2>Putting It All Together<a class="headerlink" href="#putting-it-all-together" title="Link to this heading"></a></h2>
<p>A production-ready configuration setup:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">os</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">dotenv</span><span class="w"> </span><span class="kn">import</span> <span class="n">load_dotenv</span>
<span class="n">load_dotenv</span><span class="p">()</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">responder</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="c1"># secret_key is read from RESPONDER_SECRET_KEY automatically</span>
<span class="n">debug</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">&quot;DEBUG&quot;</span><span class="p">,</span> <span class="s2">&quot;false&quot;</span><span class="p">)</span> <span class="o">==</span> <span class="s2">&quot;true&quot;</span><span class="p">,</span>
<span class="n">allowed_hosts</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">&quot;ALLOWED_HOSTS&quot;</span><span class="p">,</span> <span class="s2">&quot;*&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;,&quot;</span><span class="p">),</span>
<span class="c1"># Serving browser forms? Require a session-bound CSRF token on</span>
<span class="c1"># unsafe requests (see the tour&#39;s CSRF Protection section).</span>
<span class="n">csrf</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="c1"># Behind nginx/Caddy/a load balancer, honor its Forwarded /</span>
<span class="c1"># X-Forwarded-* headers; leave off when directly exposed.</span>
<span class="n">trust_proxy_headers</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">&quot;BEHIND_PROXY&quot;</span><span class="p">)</span> <span class="o">==</span> <span class="s2">&quot;true&quot;</span><span class="p">,</span>
<span class="n">cors</span><span class="o">=</span><span class="nb">bool</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">&quot;CORS_ORIGINS&quot;</span><span class="p">)),</span>
<span class="n">cors_params</span><span class="o">=</span><span class="p">{</span>
<span class="s2">&quot;allow_origins&quot;</span><span class="p">:</span> <span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">&quot;CORS_ORIGINS&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;,&quot;</span><span class="p">),</span>
<span class="s2">&quot;allow_methods&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;GET&quot;</span><span class="p">,</span> <span class="s2">&quot;POST&quot;</span><span class="p">,</span> <span class="s2">&quot;PUT&quot;</span><span class="p">,</span> <span class="s2">&quot;DELETE&quot;</span><span class="p">],</span>
<span class="p">},</span>
<span class="p">)</span>
</pre></div>
</div>
<p>With a <code class="docutils literal notranslate"><span class="pre">.env</span></code> file for local development:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">RESPONDER_SECRET_KEY</span><span class="o">=</span><span class="n">dev</span><span class="o">-</span><span class="n">secret</span><span class="o">-</span><span class="n">do</span><span class="o">-</span><span class="ow">not</span><span class="o">-</span><span class="n">use</span><span class="o">-</span><span class="ow">in</span><span class="o">-</span><span class="n">prod</span>
<span class="n">DEBUG</span><span class="o">=</span><span class="n">true</span>
<span class="n">ALLOWED_HOSTS</span><span class="o">=</span><span class="n">localhost</span><span class="p">,</span><span class="mf">127.0.0.1</span>
<span class="n">CORS_ORIGINS</span><span class="o">=</span><span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">localhost</span><span class="p">:</span><span class="mi">3000</span>
</pre></div>
</div>
<p>And the same variables set properly in production (via your cloud
platform’s dashboard, Docker secrets, or a secrets manager) — with a real
<code class="docutils literal notranslate"><span class="pre">RESPONDER_SECRET_KEY</span></code> generated by <code class="docutils literal notranslate"><span class="pre">secrets.token_urlsafe(32)</span></code>.</p>
<p>For running this app under a real server, see <a class="reference internal" href="deployment.html"><span class="doc">Deployment</span></a>.</p>
</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 current"><a class="current reference internal" href="#">Configuration</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#environment-variables">Environment Variables</a></li>
<li class="toctree-l2"><a class="reference internal" href="#using-env-files">Using .env Files</a></li>
<li class="toctree-l2"><a class="reference internal" href="#configuration-class-pattern">Configuration Class Pattern</a></li>
<li class="toctree-l2"><a class="reference internal" href="#secret-key">Secret Key</a></li>
<li class="toctree-l2"><a class="reference internal" href="#sessions">Sessions</a></li>
<li class="toctree-l2"><a class="reference internal" href="#debug-mode">Debug Mode</a></li>
<li class="toctree-l2"><a class="reference internal" href="#allowed-hosts">Allowed Hosts</a></li>
<li class="toctree-l2"><a class="reference internal" href="#putting-it-all-together">Putting It All Together</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="clientgen.html">Generated Clients</a></li>
<li class="toctree-l1"><a class="reference internal" href="deployment.html">Deployment</a></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="routers.html" title="previous chapter">Composing Apps with Routers</a></li>
<li>Next: <a href="clientgen.html" title="next chapter">Generated Clients</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">
&#169;2018-2026, Kenneth Reitz.
|
<a href="_sources/guide-config.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>