Flask's built-in server always 404 with SERVER_NAME set

nalzok picture nalzok · Oct 29, 2017 · Viewed 7.4k times · Source

Here is a minimal example:

from flask import Flask

app = Flask(__name__)
app.config['DEBUG'] = True
app.config['SERVER_NAME'] = 'myapp.dev:5000'


@app.route('/')
def hello_world():
    return 'Hello World!'

@app.errorhandler(404)
def not_found(error):
    print(str(error))
    return '404', 404


if __name__ == '__main__':
    app.run(debug=True)

If I set SERVER_NAME, Flask would response every URL with a 404 error, and when I comment out that line, it functions correctly again.

/Users/sunqingyao/Envs/flask/bin/python3.6 /Users/sunqingyao/Projects/play-ground/python-playground/foo/foo.py
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 422-505-438
127.0.0.1 - - [30/Oct/2017 07:19:55] "GET / HTTP/1.1" 404 -
404 Not Found: The requested URL was not found on the server.  If you entered the URL manually please check your spelling and try again.

Please note that this is not a duplicate of Flask 404 when using SERVER_NAME, since I'm not using Apache or any production web server. I'm just dealing with Flask's built-in development server.

I'm using Python 3.6.2, Flask 0.12.2, Werkzeug 0.12.2, PyCharm 2017.2.3 on macOS High Sierra, if it's relevant.

Answer

tinyhare picture tinyhare · Jun 14, 2018

When set SERVER_NAME, you should make HTTP request header 'Host' the same with it:

# curl http://127.0.0.1:5000/ -sv -H 'Host: myapp.dev:5000'
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 5000 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.35.0
> Accept: */*
> Host: myapp.dev:5000
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: text/html; charset=utf-8
< Content-Length: 13
< Server: Werkzeug/0.14.1 Python/3.6.5
< Date: Thu, 14 Jun 2018 09:34:31 GMT
<
* Closing connection 0
Hello, World!

if you use web explorer, you should access it use http://myapp.dev:5000/ and set /etc/hosts file.

It is like the nginx vhost, use Host header to do routing.

I think The SERVER_NAME is mainly used for route map.

you should set host and ip by hand

app.run(host="0.0.0.0",port=5000)

if you not set host/ip but set SERVER_NAME and found it seems to work,because the app.run() have this logic:

    def run(self, host=None, port=None, debug=None,
        load_dotenv=True, **options):

        ...

        _host = '127.0.0.1'
        _port = 5000
        server_name = self.config.get('SERVER_NAME')
        sn_host, sn_port = None, None

        if server_name:
            sn_host, _, sn_port = server_name.partition(':')

        host = host or sn_host or _host
        port = int(port or sn_port or _port)

        ...

        try:
            run_simple(host, port, self, **options)
        finally:
            self._got_first_request = False

At last, don't use SERVER_NAME to set host,ip app.run() used, unless you know its impact on the route map.