Airflow 2 129 's Flask 15,439 "webserver" is gone. Airflow 3's airflow api-server is a FastAPI 36,700 application served by Uvicorn that does three jobs: the public REST API under /api/v2 (v1 was removed), the React 7,897 UI that calls the same API, and the internal Task Execution API under /execution/ that running tasks call (Triggerer and Task Execution). Authentication is pluggable through an auth manager: the default SimpleAuthManager suits development, and the official Compose file installs the Flask AppBuilder (FAB) auth manager from its provider for users and roles. API clients exchange credentials for a JWT and send it as a bearer token:
B=http://localhost:32880
curl -s $B/api/v2/monitor/health | jq -c '{metadatabase: .metadatabase.status,
scheduler: .scheduler.status, triggerer: .triggerer.status, dag_processor: .dag_processor.status}'
TOKEN=$(curl -s -X POST $B/auth/token -H 'Content-Type: application/json' \
-d '{"username": "airflow", "password": "airflow"}' | jq -r .access_token)
curl -s $B/api/v2/version -H "Authorization: Bearer $TOKEN" | jq -c .
curl -s $B/api/v2/dags -H "Authorization: Bearer $TOKEN" \
| jq -c '.dags[] | {dag_id, is_paused, timetable_summary}'
curl -s -o /dev/null -w 'without a token: HTTP %{http_code}\n' $B/api/v2/dags{"metadatabase":"healthy","scheduler":"healthy","triggerer":"healthy","dag_processor":"healthy"
}
{"version":"3.3.2","git_version":".release:aa19d2dbb9ed187ec01957a92848d9b005e9ba9d"}
{"dag_id":"arch_demo","is_paused":true,"timetable_summary":null}
{"dag_id":"booknest_daily","is_paused":true,"timetable_summary":"0 2 * * *"}
without a token: HTTP 401The health endpoint needs no token, which makes it the right target for load balancer and container health checks. The UI's home page shows the same four health badges:

The UI is a single-page React application, so any screen you click through can be scripted against /api/v2. API servers are stateless: run two or more behind a load balancer for availability.