<p dir="auto">This is a bit of a weird one cropping up under very particular circumstances, but I spent a while last week puzzling over the issue. Probably best explained through the example.</p>
<hr>
<h3 dir="auto">Expected Behavior</h3>
<p dir="auto">Entering "Todd" into the form field in this simple example app and submitting the form takes the user to /hello/Todd, just like clicking the "Click here if your name is Todd" link does.</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="from flask import Flask, redirect, request, url_for
app = Flask(__name__)

@app.route(&quot;/&quot;)
def form():
    return &quot;&quot;&quot;
        &lt;p&gt;&lt;a href=&quot;{}&quot;&gt;Click here if your name is Todd&lt;/a&gt;&lt;p&gt;
        &lt;form action=&quot;{}&quot; method=&quot;post&quot;&gt;
            &lt;input type=&quot;text&quot; name=&quot;name&quot;&gt;
            &lt;button type=&quot;submit&quot;&gt;This is my name!&lt;/button&gt;
        &lt;/form&gt;
    &quot;&quot;&quot;.format(
        url_for(&quot;hello&quot;, name=&quot;Todd&quot;),
        url_for(&quot;hello&quot;)
    )

@app.route(&quot;/hello&quot;, methods=[&quot;GET&quot;, &quot;POST&quot;])
@app.route(&quot;/hello/&lt;name&gt;&quot;)
def hello(name=None):
    if request.method == &quot;POST&quot;:
        return redirect(url_for(&quot;hello&quot;, name=request.form.get(&quot;name&quot;)))
    if name:
        return &quot;Hello {}!&quot;.format(name)
    else:
        return &quot;Hello World!&quot;"><pre class="notranslate"><span class="pl-k">from</span> <span class="pl-s1">flask</span> <span class="pl-k">import</span> <span class="pl-v">Flask</span>, <span class="pl-s1">redirect</span>, <span class="pl-s1">request</span>, <span class="pl-s1">url_for</span>
<span class="pl-s1">app</span> <span class="pl-c1">=</span> <span class="pl-v">Flask</span>(<span class="pl-s1">__name__</span>)

<span class="pl-en">@<span class="pl-s1">app</span>.<span class="pl-en">route</span>(<span class="pl-s">"/"</span>)</span>
<span class="pl-k">def</span> <span class="pl-en">form</span>():
    <span class="pl-k">return</span> <span class="pl-s">"""</span>
<span class="pl-s">        &lt;p&gt;&lt;a href="{}"&gt;Click here if your name is Todd&lt;/a&gt;&lt;p&gt;</span>
<span class="pl-s">        &lt;form action="{}" method="post"&gt;</span>
<span class="pl-s">            &lt;input type="text" name="name"&gt;</span>
<span class="pl-s">            &lt;button type="submit"&gt;This is my name!&lt;/button&gt;</span>
<span class="pl-s">        &lt;/form&gt;</span>
<span class="pl-s">    """</span>.<span class="pl-en">format</span>(
        <span class="pl-en">url_for</span>(<span class="pl-s">"hello"</span>, <span class="pl-s1">name</span><span class="pl-c1">=</span><span class="pl-s">"Todd"</span>),
        <span class="pl-en">url_for</span>(<span class="pl-s">"hello"</span>)
    )

<span class="pl-en">@<span class="pl-s1">app</span>.<span class="pl-en">route</span>(<span class="pl-s">"/hello"</span>, <span class="pl-s1">methods</span><span class="pl-c1">=</span>[<span class="pl-s">"GET"</span>, <span class="pl-s">"POST"</span>])</span>
<span class="pl-en">@<span class="pl-s1">app</span>.<span class="pl-en">route</span>(<span class="pl-s">"/hello/&lt;name&gt;"</span>)</span>
<span class="pl-k">def</span> <span class="pl-en">hello</span>(<span class="pl-s1">name</span><span class="pl-c1">=</span><span class="pl-c1">None</span>):
    <span class="pl-k">if</span> <span class="pl-s1">request</span>.<span class="pl-s1">method</span> <span class="pl-c1">==</span> <span class="pl-s">"POST"</span>:
        <span class="pl-k">return</span> <span class="pl-en">redirect</span>(<span class="pl-en">url_for</span>(<span class="pl-s">"hello"</span>, <span class="pl-s1">name</span><span class="pl-c1">=</span><span class="pl-s1">request</span>.<span class="pl-s1">form</span>.<span class="pl-en">get</span>(<span class="pl-s">"name"</span>)))
    <span class="pl-k">if</span> <span class="pl-s1">name</span>:
        <span class="pl-k">return</span> <span class="pl-s">"Hello {}!"</span>.<span class="pl-en">format</span>(<span class="pl-s1">name</span>)
    <span class="pl-k">else</span>:
        <span class="pl-k">return</span> <span class="pl-s">"Hello World!"</span></pre></div>
<h3 dir="auto">Actual Behavior</h3>
<p dir="auto">Submitting the form instead takes you to /hello?name=Todd.</p>
<p dir="auto">Through my own testing I've concluded that <code class="notranslate">url_for("hello", name="Todd")</code> will return <code class="notranslate">/hello?name=Todd</code> if</p>
<ul dir="auto">
<li>we are currently processing a POST request, <em>and</em></li>
<li>the target endpoint has a parameter-less route with the POST method allowed (<code class="notranslate">/hello</code>), <em>but</em> the route we actually want (<code class="notranslate">/hello/&lt;name&gt;</code>) only allows GET requests.</li>
</ul>
<p dir="auto">This seems obviously bizarre and counterintuitive.</p>
<h3 dir="auto">Environment</h3>
<ul dir="auto">
<li>Python version: 3.6.4</li>
<li>Flask version: 0.12.2</li>
<li>Werkzeug version: 0.13</li>
</ul>
<p dir="auto">In response to a POST, I wanted to construct a URL pointing at a different URL (the home of the created object) which did NOT support POST.</p>
<p dir="auto">It turns out, this doesn't work with Flask==0.12.2. The rule I want doesn't match, so I end up matching a less specific rule, with the distinguishing name treated as a parameter, not a path.</p>
<p dir="auto">What appears to be happening is that default method is being pulled out of _request_ctx early in url_for(). In this case, that method will be a POST.</p>
<p dir="auto">I'm not sure if defaulting to the request's current method is really desirable. How often are you responding to a POST and want url_for to respond with the best match for POST? I think typically most endpoints will respond to a GET, but many endpoints won't respond to a POST.</p>
<p dir="auto">The workaround is to include the undocumented _method keyword argument. That will work (hopefully permanently), but I'd prefer:</p>
<p dir="auto">A) for this to be explicitly supported by the API, and<br>
B) I think the default should probably always be GET. My guess is that will be correct far more</p>