Flask
port 80
web development
Python
server configuration

How do I get Flask to run on port 80?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Running a Flask app on port 80 is a deployment concern, not just a code setting. Port 80 is privileged on Linux, so direct binding often requires elevated permissions or a reverse proxy architecture. The safest production setup is Flask behind Gunicorn and Nginx, with Nginx listening on port 80.

Development Versus Production Approach

For local development, Flask built-in server is fine and usually runs on port 5000. For production, use a WSGI server such as Gunicorn and put a reverse proxy in front.

bash
pip install flask gunicorn

Simple Flask app:

python
1from flask import Flask
2
3app = Flask(__name__)
4
5@app.get("/")
6def index():
7    return "Hello from Flask"

Run with Gunicorn on internal port:

bash
gunicorn -w 2 -b 127.0.0.1:8000 app:app

Let Nginx own port 80 and forward traffic to Gunicorn. This avoids running Python as root.

nginx
1server {
2    listen 80;
3    server_name example.com;
4
5    location / {
6        proxy_pass http://127.0.0.1:8000;
7        proxy_set_header Host $host;
8        proxy_set_header X-Real-IP $remote_addr;
9        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
10        proxy_set_header X-Forwarded-Proto $scheme;
11    }
12}

Enable config and reload Nginx.

bash
sudo ln -s /etc/nginx/sites-available/flask-app /etc/nginx/sites-enabled/flask-app
sudo nginx -t
sudo systemctl reload nginx

This setup also makes HTTPS termination and static file handling easier.

Alternative: Bind Directly to Port 80 with Capability

If you must bind Flask or Gunicorn directly to port 80 on Linux, assign network bind capability to Python binary. Use carefully and understand security implications.

bash
sudo setcap 'cap_net_bind_service=+ep' /usr/bin/python3
python3 app.py

Or bind Gunicorn directly if process permissions allow.

bash
gunicorn -w 2 -b 0.0.0.0:80 app:app

This is less common than reverse proxy setup and may complicate operations.

Do Not Run as Root by Default

Running app processes as root just to bind privileged ports is risky. A remote code execution bug becomes full system compromise. Prefer least privilege.

If you use systemd, run Gunicorn as a dedicated service user.

ini
1[Unit]
2Description=Gunicorn for Flask app
3After=network.target
4
5[Service]
6User=flaskapp
7Group=www-data
8WorkingDirectory=/opt/flask-app
9ExecStart=/opt/flask-app/.venv/bin/gunicorn -w 2 -b 127.0.0.1:8000 app:app
10Restart=always
11
12[Install]
13WantedBy=multi-user.target

Container and Cloud Environments

In Docker and Kubernetes, applications commonly listen on non-privileged internal ports like 8080, and ingress components expose port 80 externally.

dockerfile
1FROM python:3.12-slim
2WORKDIR /app
3COPY requirements.txt .
4RUN pip install -r requirements.txt
5COPY . .
6CMD ["gunicorn", "-w", "2", "-b", "0.0.0.0:8080", "app:app"]

Platform networking maps external port 80 to container port 8080.

Quick Verification Checklist

After deployment, verify each layer independently so port issues are easier to isolate.

bash
1# Gunicorn health on internal port
2curl -i http://127.0.0.1:8000/
3
4# Nginx on public port 80
5curl -i http://localhost/
6
7# Service status
8sudo systemctl status gunicorn
9sudo systemctl status nginx

If internal check passes but external fails, focus on Nginx config and firewall rules. If both fail, inspect application startup logs and Python environment paths in your service unit.

In cloud environments, also verify security group or load balancer listener configuration, since local host checks can succeed while external traffic remains blocked.

This layered verification routine shortens incident response time and helps teams separate application bugs from infrastructure misconfiguration.

Common Pitfalls

  • Using Flask development server in production.
  • Binding to port 80 by running as root.
  • Forgetting reverse proxy headers, causing incorrect URL scheme detection.
  • Exposing Gunicorn directly to the internet without proxy buffering and limits.
  • Not monitoring service restarts and health checks.

Summary

  • Port 80 is privileged, so direct Flask binding needs special permissions.
  • Production best practice is Gunicorn on internal port and Nginx on port 80.
  • Avoid root execution and prefer least-privilege service users.
  • In containers, bind app to high internal port and map externally with ingress.
  • Treat port configuration as part of deployment architecture, not only app code.

Course illustration
Course illustration

All Rights Reserved.