<p dir="auto"><strong>What is the bug?</strong></p>
<p dir="auto">When you perform the first post request, everything's fine. Each subsequent request (or a page refresh) leads to a 405 error.</p>
<p dir="auto"><strong>How to Reproduce the error</strong><br>
Here's the minimum code to replicate the error:</p>
<p dir="auto">Project Structure</p>
<ul dir="auto">
<li>templates
<ul dir="auto">
<li>index.html</li>
</ul>
</li>
<li>main.py</li>
</ul>
<p dir="auto">main.py:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="from flask import Flask, render_template
import json

app = Flask(__name__)


@app.route(&quot;/&quot;)
def home():
    return render_template(&quot;index.html&quot;)


@app.route(&quot;/post&quot;, methods=[&quot;POST&quot;])
def post_test():
    return json.dumps({&quot;test&quot;: True})


if __name__ == &quot;__main__&quot;:
    app.run(debug=True)"><pre class="notranslate"><code class="notranslate">from flask import Flask, render_template
import json

app = Flask(__name__)


@app.route("/")
def home():
    return render_template("index.html")


@app.route("/post", methods=["POST"])
def post_test():
    return json.dumps({"test": True})


if __name__ == "__main__":
    app.run(debug=True)
</code></pre></div>
<p dir="auto">index.html:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="&lt;script&gt;

    async function postSomething() {
        let options = {
            method: &quot;POST&quot;,
            body: JSON.stringify({ &quot;name&quot;: &quot;test&quot; })
        }
        let request = await fetch(&quot;/post&quot;, options);
        let response = await request.json()
        console.log(response)
    }
&lt;/script&gt;

&lt;button onclick=&quot;postSomething()&quot;&gt;
    Click me!
&lt;/button&gt;"><pre class="notranslate"><code class="notranslate">&lt;script&gt;

    async function postSomething() {
        let options = {
            method: "POST",
            body: JSON.stringify({ "name": "test" })
        }
        let request = await fetch("/post", options);
        let response = await request.json()
        console.log(response)
    }
&lt;/script&gt;

&lt;button onclick="postSomething()"&gt;
    Click me!
&lt;/button&gt;
</code></pre></div>
<p dir="auto"><strong>What do I expect</strong><br>
I expect to be able to press the button multiple times without getting an error. It seems that Flask puts the form data before the request, which leads to this in the console:</p>
<p dir="auto"><code class="notranslate">127.0.0.1 - - [10/Sep/2022 12:09:52] "{"name":"test"}GET / HTTP/1.1" 405 -</code></p>
<p dir="auto">I tested the same behaviour with Tornado, which gave me the correct expected result. Here's the minimal code for Tornado:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="import asyncio
import tornado.web
import tornado.template


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render(&quot;templates/index.html&quot;)


class PostHandler(tornado.web.RequestHandler):
    def post(self):
        self.write({&quot;test&quot;: True})


async def main():
    application = tornado.web.Application(
        [(r&quot;/&quot;, MainHandler), (r&quot;/post&quot;, PostHandler)],
        debug=True,
    )
    application.listen(5000)
    await asyncio.Event().wait()


if __name__ == &quot;__main__&quot;:
    print(&quot;Running Main&quot;)
    asyncio.run(main())
"><pre class="notranslate"><code class="notranslate">import asyncio
import tornado.web
import tornado.template


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("templates/index.html")


class PostHandler(tornado.web.RequestHandler):
    def post(self):
        self.write({"test": True})


async def main():
    application = tornado.web.Application(
        [(r"/", MainHandler), (r"/post", PostHandler)],
        debug=True,
    )
    application.listen(5000)
    await asyncio.Event().wait()


if __name__ == "__main__":
    print("Running Main")
    asyncio.run(main())

</code></pre></div>
<p dir="auto">Environment:</p>
<ul dir="auto">
<li>Python version: 3.9.13</li>
<li>Flask version: 2.2.2</li>
</ul>
<p dir="auto">With the following example:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="from flask import Flask

app = Flask(__name__)


@app.route('/', methods=['POST'])
def hello_world():
    return 'Hello World!'


if __name__ == '__main__':
    app.run()"><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">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 class="pl-s1">methods</span><span class="pl-c1">=</span>[<span class="pl-s">'POST'</span>])</span>
<span class="pl-k">def</span> <span class="pl-en">hello_world</span>():
    <span class="pl-k">return</span> <span class="pl-s">'Hello World!'</span>


<span class="pl-k">if</span> <span class="pl-s1">__name__</span> <span class="pl-c1">==</span> <span class="pl-s">'__main__'</span>:
    <span class="pl-s1">app</span>.<span class="pl-en">run</span>()</pre></div>
<p dir="auto">When you set the request body to <code class="notranslate">{}</code> with Postman or any HTTP clients, the first request will return 200, while the second request will return a 405 error response. The log shows the request method is <code class="notranslate">{}POST</code>:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="&quot;{}POST / HTTP/1.1&quot; 405"><pre class="notranslate"><code class="notranslate">"{}POST / HTTP/1.1" 405
</code></pre></div>
<p dir="auto">Notice the request body became the part of the request method.</p>