nalzok
nalzok

Reputation: 16137

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

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.

Upvotes: 12

Views: 17986

Answers (5)

tinyhare
tinyhare

Reputation: 2401

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.

Upvotes: 7

kip2
kip2

Reputation: 6893

Sometimes I find Flask's docs to be confusing (see the quotes above by @dm295 - the meaning of the implications surrounding 'SERVER_NAME' is hard to parse). But an alternative setup to (and inspired by) @Dancer Phd's answer is to specify the 'HOST' and 'PORT' parameters in a config file instead of 'SERVER_NAME'.

For example, let's say you use this config strategy proposed in the Flask docs, add the host & port number like so:

class Config(object):
    DEBUG = False
    TESTING = False
    DATABASE_URI = 'sqlite://:memory:'
    HOST = 'http://localhost' #
    PORT = '5000'

class ProductionConfig(Config):
    DATABASE_URI = 'mysql://user@localhost/foo'

class DevelopmentConfig(Config):
    DEBUG = True

class TestingConfig(Config):
    TESTING = True 

Upvotes: 3

Dancer PhD
Dancer PhD

Reputation: 307

You can also use just port number and host inside of the app.run like:

app.run(debug=True, port=5000, host="localhost")

Delete:

app.config['DEBUG'] = True
app.config['SERVER_NAME'] = 'myapp.dev:5000'

Upvotes: 0

tanjibpa
tanjibpa

Reputation: 127

Using debug=True worked for me:

from flask import Flask


app = Flask(__name__)
app.config['SERVER_NAME'] = 'localhost:5000'

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


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

Upvotes: -3

Dušan Maďar
Dušan Maďar

Reputation: 9909

From Flask docs:

the name and port number of the server. Required for subdomain support (e.g.: 'myapp.dev:5000') Note that localhost does not support subdomains so setting this to “localhost” does not help. Setting a SERVER_NAME also by default enables URL generation without a request context but with an application context.

and

More on SERVER_NAME

The SERVER_NAME key is used for the subdomain support. Because Flask cannot guess the subdomain part without the knowledge of the actual server name, this is required if you want to work with subdomains. This is also used for the session cookie.

Please keep in mind that not only Flask has the problem of not knowing what subdomains are, your web browser does as well. Most modern web browsers will not allow cross-subdomain cookies to be set on a server name without dots in it. So if your server name is 'localhost' you will not be able to set a cookie for 'localhost' and every subdomain of it. Please choose a different server name in that case, like 'myapplication.local' and add this name + the subdomains you want to use into your host config or setup a local bind.

It looks like there's no point to setting it to localhost. As suggested in the docs, try something like myapp.dev:5000.

Upvotes: 1

Related Questions