<p dir="auto">I made a test code for this issue. I made a custom class because I want server to return jsonified list if view function returns list. It works on flask 1.0.2 but it doesn't work on the new flask version (1.1.1)</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="from flask import Flask, Response, jsonify


class CustomResponse(Response):
    @classmethod
    def force_type(cls, response, environ=None):
        if isinstance(response, list):
            return jsonify(response)
        return super(CustomResponse, cls).force_type(response, environ)


app = Flask(__name__)
app.response_class = CustomResponse


@app.route('/', methods=['GET'])
def response_list():
    return ['it', 'is', 'test']


if __name__ == '__main__':
    app.run(host='127.0.0.1')
"><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-v">Response</span>, <span class="pl-s1">jsonify</span>


<span class="pl-k">class</span> <span class="pl-v">CustomResponse</span>(<span class="pl-v">Response</span>):
    <span class="pl-en">@<span class="pl-s1">classmethod</span></span>
    <span class="pl-k">def</span> <span class="pl-en">force_type</span>(<span class="pl-s1">cls</span>, <span class="pl-s1">response</span>, <span class="pl-s1">environ</span><span class="pl-c1">=</span><span class="pl-c1">None</span>):
        <span class="pl-k">if</span> <span class="pl-en">isinstance</span>(<span class="pl-s1">response</span>, <span class="pl-s1">list</span>):
            <span class="pl-k">return</span> <span class="pl-en">jsonify</span>(<span class="pl-s1">response</span>)
        <span class="pl-k">return</span> <span class="pl-en">super</span>(<span class="pl-v">CustomResponse</span>, <span class="pl-s1">cls</span>).<span class="pl-en">force_type</span>(<span class="pl-s1">response</span>, <span class="pl-s1">environ</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-s1">app</span>.<span class="pl-s1">response_class</span> <span class="pl-c1">=</span> <span class="pl-v">CustomResponse</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">'GET'</span>])</span>
<span class="pl-k">def</span> <span class="pl-en">response_list</span>():
    <span class="pl-k">return</span> [<span class="pl-s">'it'</span>, <span class="pl-s">'is'</span>, <span class="pl-s">'test'</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>(<span class="pl-s1">host</span><span class="pl-c1">=</span><span class="pl-s">'127.0.0.1'</span>)</pre></div>
<p dir="auto">On 1.0.2 version,</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="# evaluate a WSGI callable, or coerce a different response
                # class to the correct type
                try:
                    rv = self.response_class.force_type(rv, request.environ)
                except TypeError as e:
                    new_error = TypeError(
                        '{e}\nThe view function did not return a valid'
                        ' response. The return type must be a string, tuple,'
                        ' Response instance, or WSGI callable, but it was a'
                        ' {rv.__class__.__name__}.'.format(e=e, rv=rv)
                    )"><pre class="notranslate"><span class="pl-c"># evaluate a WSGI callable, or coerce a different response</span>
                <span class="pl-c"># class to the correct type</span>
                <span class="pl-k">try</span>:
                    <span class="pl-s1">rv</span> <span class="pl-c1">=</span> <span class="pl-s1">self</span>.<span class="pl-s1">response_class</span>.<span class="pl-en">force_type</span>(<span class="pl-s1">rv</span>, <span class="pl-s1">request</span>.<span class="pl-s1">environ</span>)
                <span class="pl-k">except</span> <span class="pl-v">TypeError</span> <span class="pl-k">as</span> <span class="pl-s1">e</span>:
                    <span class="pl-s1">new_error</span> <span class="pl-c1">=</span> <span class="pl-v">TypeError</span>(
                        <span class="pl-s">'{e}<span class="pl-cce">\n</span>The view function did not return a valid'</span>
                        <span class="pl-s">' response. The return type must be a string, tuple,'</span>
                        <span class="pl-s">' Response instance, or WSGI callable, but it was a'</span>
                        <span class="pl-s">' {rv.__class__.__name__}.'</span>.<span class="pl-en">format</span>(<span class="pl-s1">e</span><span class="pl-c1">=</span><span class="pl-s1">e</span>, <span class="pl-s1">rv</span><span class="pl-c1">=</span><span class="pl-s1">rv</span>)
                    )</pre></div>
<p dir="auto">flask changes type forcely by calling force_type of <code class="notranslate">response_class</code>, but newest version doesn't change. (code below)</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="            if isinstance(rv, (text_type, bytes, bytearray)):
                # let the response class set the status and headers instead of
                # waiting to do it manually, so that the class can handle any
                # special logic
                rv = self.response_class(rv, status=status, headers=headers)
                status = headers = None
            elif isinstance(rv, dict):
                rv = jsonify(rv)
            elif isinstance(rv, BaseResponse) or callable(rv):
                # evaluate a WSGI callable, or coerce a different response
                # class to the correct type
                try:
                    rv = self.response_class.force_type(rv, request.environ)
                except TypeError as e:
                    new_error = TypeError(
                        &quot;{e}\nThe view function did not return a valid&quot;
                        &quot; response. The return type must be a string, dict, tuple,&quot;
                        &quot; Response instance, or WSGI callable, but it was a&quot;
                        &quot; {rv.__class__.__name__}.&quot;.format(e=e, rv=rv)
                    )
                    reraise(TypeError, new_error, sys.exc_info()[2])
            else:
                raise TypeError(
                    &quot;The view function did not return a valid&quot;
                    &quot; response. The return type must be a string, dict, tuple,&quot;
                    &quot; Response instance, or WSGI callable, but it was a&quot;
                    &quot; {rv.__class__.__name__}.&quot;.format(rv=rv)
                )"><pre class="notranslate">            <span class="pl-k">if</span> <span class="pl-en">isinstance</span>(<span class="pl-s1">rv</span>, (<span class="pl-s1">text_type</span>, <span class="pl-s1">bytes</span>, <span class="pl-s1">bytearray</span>)):
                <span class="pl-c"># let the response class set the status and headers instead of</span>
                <span class="pl-c"># waiting to do it manually, so that the class can handle any</span>
                <span class="pl-c"># special logic</span>
                <span class="pl-s1">rv</span> <span class="pl-c1">=</span> <span class="pl-s1">self</span>.<span class="pl-en">response_class</span>(<span class="pl-s1">rv</span>, <span class="pl-s1">status</span><span class="pl-c1">=</span><span class="pl-s1">status</span>, <span class="pl-s1">headers</span><span class="pl-c1">=</span><span class="pl-s1">headers</span>)
                <span class="pl-s1">status</span> <span class="pl-c1">=</span> <span class="pl-s1">headers</span> <span class="pl-c1">=</span> <span class="pl-c1">None</span>
            <span class="pl-k">elif</span> <span class="pl-en">isinstance</span>(<span class="pl-s1">rv</span>, <span class="pl-s1">dict</span>):
                <span class="pl-s1">rv</span> <span class="pl-c1">=</span> <span class="pl-en">jsonify</span>(<span class="pl-s1">rv</span>)
            <span class="pl-k">elif</span> <span class="pl-en">isinstance</span>(<span class="pl-s1">rv</span>, <span class="pl-v">BaseResponse</span>) <span class="pl-c1">or</span> <span class="pl-en">callable</span>(<span class="pl-s1">rv</span>):
                <span class="pl-c"># evaluate a WSGI callable, or coerce a different response</span>
                <span class="pl-c"># class to the correct type</span>
                <span class="pl-k">try</span>:
                    <span class="pl-s1">rv</span> <span class="pl-c1">=</span> <span class="pl-s1">self</span>.<span class="pl-s1">response_class</span>.<span class="pl-en">force_type</span>(<span class="pl-s1">rv</span>, <span class="pl-s1">request</span>.<span class="pl-s1">environ</span>)
                <span class="pl-k">except</span> <span class="pl-v">TypeError</span> <span class="pl-k">as</span> <span class="pl-s1">e</span>:
                    <span class="pl-s1">new_error</span> <span class="pl-c1">=</span> <span class="pl-v">TypeError</span>(
                        <span class="pl-s">"{e}<span class="pl-cce">\n</span>The view function did not return a valid"</span>
                        <span class="pl-s">" response. The return type must be a string, dict, tuple,"</span>
                        <span class="pl-s">" Response instance, or WSGI callable, but it was a"</span>
                        <span class="pl-s">" {rv.__class__.__name__}."</span>.<span class="pl-en">format</span>(<span class="pl-s1">e</span><span class="pl-c1">=</span><span class="pl-s1">e</span>, <span class="pl-s1">rv</span><span class="pl-c1">=</span><span class="pl-s1">rv</span>)
                    )
                    <span class="pl-en">reraise</span>(<span class="pl-v">TypeError</span>, <span class="pl-s1">new_error</span>, <span class="pl-s1">sys</span>.<span class="pl-en">exc_info</span>()[<span class="pl-c1">2</span>])
            <span class="pl-k">else</span>:
                <span class="pl-k">raise</span> <span class="pl-v">TypeError</span>(
                    <span class="pl-s">"The view function did not return a valid"</span>
                    <span class="pl-s">" response. The return type must be a string, dict, tuple,"</span>
                    <span class="pl-s">" Response instance, or WSGI callable, but it was a"</span>
                    <span class="pl-s">" {rv.__class__.__name__}."</span>.<span class="pl-en">format</span>(<span class="pl-s1">rv</span><span class="pl-c1">=</span><span class="pl-s1">rv</span>)
                )</pre></div>
<p dir="auto">Is it intended or just a bug?</p>
<h3 dir="auto">Expected</h3>
<p dir="auto">When I call <code class="notranslate">127.0.0.1:5000</code>,</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="['it', 'is', 'test']"><pre class="notranslate"><code class="notranslate">['it', 'is', 'test']
</code></pre></div>
<h3 dir="auto">Actual</h3>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="[2019-07-10 16:36:33,554] ERROR in app: Exception on / [GET]
Traceback (most recent call last):
  File &quot;C:\Users\Mapia\Desktop\develop\study\flask.py\venv\lib\site-packages\flask\app.py&quot;, line 2446, in wsgi_app
    response = self.full_dispatch_request()
  File &quot;C:\Users\Mapia\Desktop\develop\study\flask.py\venv\lib\site-packages\flask\app.py&quot;, line 1952, in full_dispatch_request
    return self.finalize_request(rv)
  File &quot;C:\Users\Mapia\Desktop\develop\study\flask.py\venv\lib\site-packages\flask\app.py&quot;, line 1967, in finalize_request
    response = self.make_response(rv)
  File &quot;C:\Users\Mapia\Desktop\develop\study\flask.py\venv\lib\site-packages\flask\app.py&quot;, line 2130, in make_response
    &quot; {rv.__class__.__name__}.&quot;.format(rv=rv)
TypeError: The view function did not return a valid response. The return type must be a string, dict, tuple, Response instance, or WSGI callable, but it was a list.
127.0.0.1 - - [10/Jul/2019 16:36:33] &quot;GET / HTTP/1.1&quot; 500 -"><pre class="notranslate"><code class="notranslate">[2019-07-10 16:36:33,554] ERROR in app: Exception on / [GET]
Traceback (most recent call last):
  File "C:\Users\Mapia\Desktop\develop\study\flask.py\venv\lib\site-packages\flask\app.py", line 2446, in wsgi_app
    response = self.full_dispatch_request()
  File "C:\Users\Mapia\Desktop\develop\study\flask.py\venv\lib\site-packages\flask\app.py", line 1952, in full_dispatch_request
    return self.finalize_request(rv)
  File "C:\Users\Mapia\Desktop\develop\study\flask.py\venv\lib\site-packages\flask\app.py", line 1967, in finalize_request
    response = self.make_response(rv)
  File "C:\Users\Mapia\Desktop\develop\study\flask.py\venv\lib\site-packages\flask\app.py", line 2130, in make_response
    " {rv.__class__.__name__}.".format(rv=rv)
TypeError: The view function did not return a valid response. The return type must be a string, dict, tuple, Response instance, or WSGI callable, but it was a list.
127.0.0.1 - - [10/Jul/2019 16:36:33] "GET / HTTP/1.1" 500 -
</code></pre></div>
<h3 dir="auto">Environment</h3>
<ul dir="auto">
<li>Python version: 3.7.3</li>
<li>Flask version: 1.1.1</li>
<li>Werkzeug version: 0.15.4</li>
</ul>
<h3 dir="auto">Expected Behavior</h3>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="from flask import Flask
app = Flask(__name__)
@app.route('/')
def issue():
    return {'test': 'test'}, {'Content-Type': 'test'}"><pre class="notranslate"><code class="notranslate">from flask import Flask
app = Flask(__name__)
@app.route('/')
def issue():
    return {'test': 'test'}, {'Content-Type': 'test'}
</code></pre></div>
<p dir="auto">Using <code class="notranslate">curl -v http://127.0.0.1:5000/</code> to query the view I expect only one <code class="notranslate">Content-Type</code> header &gt; <code class="notranslate">Content-Type: test</code></p>
<h3 dir="auto">Actual Behavior</h3>
<p dir="auto">Duplicate headers are returned</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="&lt; Content-Type: application/json
&lt; Content-Type: test"><pre class="notranslate"><code class="notranslate">&lt; Content-Type: application/json
&lt; Content-Type: test
</code></pre></div>
<h3 dir="auto">Environment</h3>
<ul dir="auto">
<li>Python version: 3.8.2</li>
<li>Flask version: 1.1.2</li>
<li>Werkzeug version: 1.0.1</li>
</ul>
<h3 dir="auto">Context</h3>
<p dir="auto">This issue also effects responses created with make_response when using a dict or jsonify body + the headers argument with a 'Content-Type':</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="from flask import Flask, make_response
app = Flask(__name__)
@app.route('/')
def issue():
    return make_response({'test': 'test'}, {'Content-Type': 'test'})"><pre class="notranslate"><code class="notranslate">from flask import Flask, make_response
app = Flask(__name__)
@app.route('/')
def issue():
    return make_response({'test': 'test'}, {'Content-Type': 'test'})
</code></pre></div>
<p dir="auto">This issue is caused by jsonify adding a 'Content-Type' header then make_response uses <code class="notranslate">extent</code> to add the additional headers, thus leading to the duplicate.</p>
<p dir="auto">Returning a str/bytes body does not have this problem as no 'Content-Type' is added by flask, if one is missing it is added by werkzeug.</p>
<p dir="auto">The reason I came across this issue is we have older code which does <code class="notranslate">return json.dumps(data), 200, {'Content-Type': 'application/json+somecustomtype'}</code> and I assumed based on the flask docs that just returning the data and letting flask do the jsonify would be better.</p>