Table of Contents
If you’ve used web frameworks like Flask or FastAPI, you may have noticed something curious: people call Flask a WSGI framework and FastAPI a ASGI framework. But what does that really mean? Why do we have these different interfaces in Python web development, and how should you choose between them?
In this article, we’ll break down the history, architecture, and differences between WSGI and ASGI, and explain why frameworks like Flask, Django, Starlette and FastAPI exist.




From CGI to WSGI: The Synchronous Era
In the early days of the web, Python web apps communicated with servers using CGI (Common Gateway Interface). Each incoming request launched a brand-new operating system process, ran the application code, generated a response, and then terminated the process. While simple and portable, this was extremely inefficient—process creation was expensive, memory was wasted, and high-traffic sites quickly became bottlenecked.


To improve performance, FastCGI emerged. Instead of spawning a fresh process per request, FastCGI kept long-running worker processes alive and reused them for multiple requests. This reduced overhead, but the ecosystem was fragmented and interoperability across frameworks and servers remained messy.
Enter WSGI (Web Server Gateway Interface), standardized in PEP 3333 . WSGI became the universal contract between web servers (like Gunicorn or uWSGI) and Python frameworks (like Flask, Django, or Pyramid). It brought much-needed stability and consistency:
- ✓ Servers could run any WSGI app.
- ✓ Frameworks no longer needed to worry about server details.
- ✓ A thriving ecosystem of tools, middleware, and deployment patterns flourished.
For over a decade, WSGI was the backbone of Python web development, powering the synchronous era of the web.

The Rise of ASGI: The Asynchronous Era
As web applications became more complex and real-time features became essential, the limitations of WSGI became apparent. Modern applications needed:
- ✓ Real-time communication (chat apps, live dashboards).
- ✓ WebSockets (persistent two-way connections).
- ✓ Streaming responses (sending chunks of data as they’re ready).
- ✓ Concurrency at scale (handling thousands of simultaneous users).
But WSGI is synchronous by design — it expects one request in, one response out. Long-lived or asynchronous tasks block the whole pipeline.
To fix this, the community created ASGI (Asynchronous Server Gateway Interface). Think of ASGI as the spiritual successor to WSGI, designed for the async era. It’s flexible, supporting multiple protocols (HTTP, WebSocket, MQTT, etc.), and works seamlessly with Python’s asyncio.
Key Differences: WSGI vs ASGI
| Feature | WSGI | ASGI |
|---|---|---|
| Year Introduced | 2003 (PEP 3333) | 2016+ (PEP 484, PEP 800 series) |
| Nature | Synchronous | Asynchronous + synchronous |
| Protocols | HTTP only | HTTP, WebSockets, others |
| Concurrency | Thread/process-based | Async I/O (async/await) |
| Use Cases | CRUD apps, APIs, traditional web | Realtime apps, streaming, APIs |
| Examples | Flask, Django (default), Pyramid | Starlette, FastAPI, Django (channels) |
Examples in Practice
Flask (WSGI)
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello, WSGI World!"
Flask handles requests synchronously. Perfect for APIs or apps where requests finish quickly.
Starlette (ASGI)
from starlette.applications import Starlette
from starlette.responses import PlainTextResponse
import uvicorn
app = Starlette()
@app.route("/")
async def hello(request):
return PlainTextResponse("Hello, ASGI World!")
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
Starlette supports async functions and WebSockets out of the box. Built for highly concurrent, real-time applications.
Beyond WSGI and ASGI
While WSGI and ASGI dominate as the two main standards in Python web backends, they are not the whole picture. The web ecosystem is diverse, and not every application fits neatly into the classic request–response model. Let’s look at some important alternatives and related technologies.
1. The uWSGI Protocol (Not the Same as WSGI!)
A common source of confusion is between WSGI and uWSGI. Despite the similar names, they are different things:
- WSGI is a Python standard (an interface contract between web servers and apps).
- uWSGI is a web server and application container that implements WSGI (and more).
The uWSGI project also introduced the uWSGI protocol, a high-performance binary protocol for communication between the uWSGI server and upstream servers like Nginx.
In other words:
- WSGI = a contract.
- uWSGI = a server + a protocol.
Example workflow:
- Your Flask app (WSGI) runs on uWSGI.
- uWSGI speaks the uWSGI protocol with Nginx for speed.
- Nginx handles load balancing and static files.
This distinction matters because many people mistakenly think “uWSGI” and “WSGI” are the same — they’re not. One is a standard, the other is an implementation with its own protocol.
2. GraphQL Frameworks (Ariadne, Strawberry, Graphene)
Traditional REST APIs (built on WSGI or ASGI) follow the request–response cycle: a client asks for a resource (e.g., /users/5), and the server responds with JSON.
But GraphQL flips this model:
- Clients can specify exactly what data they need.
- The server exposes a schema that describes all possible queries.
- One request can pull related data (e.g., a user and their posts and their comments).
In Python, frameworks like Ariadne, Strawberry, and Graphene provide GraphQL servers. Under the hood, they still run on WSGI or ASGI servers, but the application model is different.
👉 Why it exists: REST is rigid for complex data needs. GraphQL solves overfetching (getting too much data) and underfetching (having to call multiple endpoints).
Example: Instead of
GET /user/5
GET /user/5/posts
GET /user/5/comments
you can do:
{
user(id: 5) {
name
posts {
title
}
comments {
body
}
}
}
3. RPC and gRPC
Sometimes, you don’t even want “web-style” APIs at all. You want remote procedure calls (RPC): a client calls a function directly on a server, as if it were local.
- Classic RPC frameworks allow you to define service interfaces and expose them to clients.
- gRPC (from Google) is a modern, high-performance RPC framework. It uses Protocol Buffers (Protobuf) for serialization (smaller and faster than JSON).
With gRPC, you define services like this:
service UserService {
rpc GetUser (UserRequest) returns (UserResponse);
}
The client then calls GetUser() as if it were a local function.
In Python, you can implement gRPC servers and clients using the official grpcio library.
👉 Why it exists: gRPC is popular in microservices architectures because it’s:
- Efficient (binary serialization, HTTP/2).
- Strongly typed (Protobuf schemas).
- Great for inter-service communication (machine-to-machine).
While REST/GraphQL are more human-friendly, gRPC is machine-friendly.
4. Serverless and Function-as-a-Service
Modern deployment models have introduced new ways to think about web applications:
- AWS Lambda, Google Cloud Functions, Azure Functions: Run code without managing servers.
- Vercel, Netlify Functions: Deploy Python functions that respond to HTTP events.
- Serverless frameworks: Tools like Zappa and Serverless Framework that package WSGI/ASGI apps for serverless deployment.
These platforms often abstract away the WSGI/ASGI layer entirely, focusing on event-driven execution.
5. WebAssembly (WASM) and Python
An emerging trend is running Python in the browser through WebAssembly:
- Pyodide: Runs Python in the browser using WebAssembly.
- PyScript: Allows you to write Python code directly in HTML.
- Brython: A Python 3 implementation for client-side web programming.
While not directly related to WSGI/ASGI, these technologies represent another evolution in how Python can be used for web development.
Putting It Together
- WSGI/ASGI → General-purpose web backends, focused on HTTP.
- uWSGI protocol → A server-side optimization for deploying Python apps (not a standard).
- GraphQL frameworks → A new application model for APIs where clients control the data shape.
- RPC/gRPC → A different communication paradigm (function calls instead of HTTP requests), great for microservices.
- Serverless → Event-driven execution without server management.
- WebAssembly → Running Python in the browser.
Each of these exists because different problems need different tools. A chat app, a scientific microservice cluster, and a public API all have very different requirements — and Python’s ecosystem has grown to support them all.
Conclusion
The evolution from CGI to WSGI to ASGI represents Python’s adaptation to changing web requirements. WSGI brought standardization and stability to the synchronous web era, while ASGI opened the door to real-time, high-concurrency applications.
Understanding these protocols helps you:
- Choose the right framework for your project.
- Optimize your application’s performance.
- Make informed decisions about deployment and scaling.
- Navigate the broader Python web ecosystem.
Whether you’re building a simple REST API with Flask, a real-time chat application with FastAPI, or a microservice with gRPC, Python’s web ecosystem has the tools you need. The key is understanding which tool fits your specific use case.
