macOS Safari displays blank page when accessing flask development server on localhost:5000

There is a very strange behavior in macOS Ventura 13.1 (and possibly earlier macOS as well), where the macOS Safari browser and the Google Chrome browser cannot access a Python Flask web development server on port 5000. The URL http://localhost:5000 either shows as completely blank, or responds with a 403 Forbidden.

Context

In short, Flask is a lightweight web application framework for Python. A typical Flask webapp is run in development mode as

flask --debug run

By default, this will start a Flask development server on port 5000:

* Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit

From the command line, subsequently, curl can be used to reach the server

curl "http://localhost:5000/"

In my case, it responds with

Hello world

Note that localhost and 127.0.0.1 are, in typical cases, equivalent to each other. Both of them are pointing to the local machine.

Problem

If using the Safari browser on the same machine, though, and hitting http://localhost:5000, this will display a completely blank page. The development server itself will not show that a GET request was made, which suggests that the request never made it to the flask application at all.

If using the Chrome browser to hit http://localhost:5000, it will display a 403 Forbidden error instead of a blank page:

Access to localhost was denied
You don't have authorization to view this page.
HTTP ERROR 403

And again, the Flask dev server itself does not show a successful GET request being made.

As a final symptom, if instead http://127.0.0.1:5000 was used, a GET will be successfully received by the Flask dev server, and the server will respond to it normally.

Solution

  1. On macOS Ventura, go to Settings -> General -> AirDrop & Handoff and turn off AirPlay Receiver.
  2. Alternatively, instruct Flask to use a different port for its dev server other than 5000 and access that port instead:
    flask --debug run -p 8000
  3. Alternatively, always interact with Flask on the IP literal 127.0.0.1:5000 rather than the localhost DNS name. This is what the Flask instructions say to do anyway.

Discussion

It turns out there is a port conflict, of sorts. On macOS Ventura (and possibly Monterey as well), the OS’s built-in AirPlay Receiver is also listening on TCP port 5000 for some purpose. Running

lsof -i -P

...
ControlCe  423 yliu    7u  IPv4       0t0  TCP *:5000 (LISTEN)
ControlCe  423 yliu    8u  IPv6       0t0  TCP *:5000 (LISTEN)
...
Python    1374 yliu    5u  IPv4       0t0  TCP localhost:5000 (LISTEN)

we can see that ControlCenter (really AirPlay Receiver) is listening on TCP *:5000 and the Python Flask app is listening on localhost:5000.

In fact, if the Flask server is turned off, and a curl -i command (to show response headers) is issued to localhost:5000, this is the response:
curl -i "http://localhost:5000/"

HTTP/1.1 404 Not Found
Content-Length: 0
Server: AirTunes/670.6.2

I’m not sure why this situation isn’t causing a port conflict, throwing a socket error for “address already in use”, which would block the Python app from starting at all. Similarly, I don’t understand why it doesn’t affect curl when run on the command line, which still successfully reaches the Flask server.

Nevertheless, changing Flask’s socket to an unoccupied port such as 8000, or turning off the offending macOS component AirPlay Receiver that is using the port, addresses the conflict. Now both Safari and Chrome will be able send requests to the actual Flask server, rather than the AirTunes server also residing on port 5000.

1 Reply to “macOS Safari displays blank page when accessing flask development server on localhost:5000”

Leave a Reply

Your email address will not be published. Required fields are marked *