localhost:5000 is the default port of Flask's development server, and on a Mac it's also where the AirPlay Receiver listens. That overlap is the most common reason http://localhost:5000 shows a blank 403 Forbidden or Flask fails with Address already in use even though nothing of yours is running. ASP.NET Core's Kestrel (when no URL is configured) and the Docker Registry image also default to 5000.
What runs on port 5000
| Tool | Default on 5000? | Start command |
|---|---|---|
| Flask | Yes | flask run or python app.py with app.run() |
macOS AirPlay Receiver (ControlCenter) | Yes, since Monterey | On by default |
| ASP.NET Core (Kestrel) | Fallback when no URL is set | dotnet run |
| Docker Registry | Yes | docker run -p 5000:5000 registry:2 |
Newer dotnet new templates set their own ports in launchSettings.json, so check that file before assuming 5000. Django and FastAPI use localhost:8000.
The macOS AirPlay Receiver conflict
Since macOS 12 Monterey, the AirPlay Receiver listens on ports 5000 and 7000. Symptoms:
- Flask prints
Address already in use. Port 5000 is in use by another program. http://localhost:5000returns 403 with aServer: AirTunesheader, even with Flask stopped.lsof -i :5000showsControlCenter.
You can't kill ControlCenter; it comes straight back. Pick one fix:
- Turn AirPlay Receiver off: System Settings, then General, then AirDrop & Handoff, then switch off AirPlay Receiver.
- Move Flask:
flask run --port 5001. This is the simplest choice for a team, because it doesn't depend on everyone's Mac settings.
Fix "localhost:5000 refused to connect"
- Check Flask is running. It prints
Running on http://127.0.0.1:5000. Ifflask runcan't find the app, setFLASK_APPor useflask --app app run. - Use the URL Flask printed. Flask binds to
127.0.0.1by default. If you started it on another port to dodge AirPlay, open that port. - Docker. Inside a container Flask must run with
--host 0.0.0.0, and the port needs-p 5000:5000. On a Mac, prefer-p 5001:5000so AirPlay doesn't get in the way on the host side.
Find and free a busy port 5000
# macOS / Linux: who owns 5000?
lsof -i :5000
kill -9 $(lsof -ti :5000) # won't help if it's ControlCenter
# Windows
netstat -ano | findstr :5000
taskkill /PID <pid> /F
Change the Flask port with flask run --port 5001, FLASK_RUN_PORT=5001, or app.run(port=5001).
Open localhost:5000 from another device
flask run --host 0.0.0.0
Then open http://YOUR-LAN-IP:5000 on a device on the same Wi-Fi. Don't do this with --debug on an untrusted network: the Werkzeug debugger lets anyone who can trigger an error run Python on your machine, protected only by a PIN.
Receive webhooks on localhost:5000
Flask is a common choice for a quick webhook receiver, but Stripe, Shopify or GitHub can't post to localhost. Forward them to your route:
relay forward --bucket flask-hooks http://localhost:5000/webhooks
Paste the bucket's public URL into the provider's webhook settings. Each request is delivered to Flask and recorded, so you can inspect the payload and replay it after you change your code. Read the raw body with request.get_data() before parsing JSON if you verify the signature; re-serialised JSON won't match the HMAC. The Shopify webhooks on Flask post is a full walkthrough.
To show the whole app to someone, use a tunnel instead:
relay connect --name my-flask-app http://localhost:5000
It prints a public HTTPS URL that stays the same if you reuse the name.
