← All articles

WSGI vs ASGI Protocols

Comprehensive guide comparing WSGI and ASGI protocols in Python web development. Learn key differences, implementation examples, and when to use each protocol for optimal web application performance.

2 min read

Python web applications require protocols to communicate between web servers and application code. WSGI (Web Server Gateway Interface) and ASGI (Asynchronous Server Gateway Interface) are two such protocols, each with distinct characteristics and use cases.

WSGI: Synchronous Web Applications

WSGI is the traditional protocol for Python web applications, introduced in PEPPython Enhancement Proposal 333peps.python.orgPEP 333, the original 2003 specification of WSGI, the interface between Python web servers and applications. and updated for Python 3 in PEP 3333peps.python.orgPEP 3333, the update of WSGI for Python 3, which clarifies how strings and bytes are handled.. Its application interface is synchronous; a server can still handle concurrent requests using threads, processes, or compatible worker implementations.

Core Concepts

  • Single callable function (application object)
  • Synchronous request-response cycle
  • Compatible with most traditional web frameworks

Example WSGI Application

# wsgi_app.py
def simple_wsgi_app(environ, start_response):
    status = '200 OK'
    headers = [('Content-type', 'text/plain')]
    start_response(status, headers)

    return [b"Hello from WSGI!"]

# Test the WSGI app
def mock_start_response(status, headers):
    print(f"Status: {status}")
    print(f"Headers: {headers}")

response = simple_wsgi_app({}, mock_start_response)
print(response[0].decode())
gunicorn --workers=2 --bind 127.0.0.1:8000 wsgi_app:simple_wsgi_app

ASGI: Modern Async Applications

ASGI is a separate asynchronous interface designed as a successor to WSGI. Its protocol specifications cover HTTPHypertext Transfer Protocol and WebSocketsA protocol that keeps one connection open so that client and server can send each other messages at any time.; actual HTTP/2 support depends on the server. See the ASGI specificationasgi.readthedocs.ioThe ASGI specification: the asynchronous interface between Python web servers, frameworks and applications..

Key Features

  • Async-first design
  • WebSocket support
  • HTTP/2 compatibility
  • Better handling of long-lived connections

Example ASGI Application

# asgi_app.py
import asyncio

async def simple_asgi_app(scope, receive, send):
    assert scope['type'] == 'http'

    await send({
        'type': 'http.response.start',
        'status': 200,
        'headers': [
            [b'content-type', b'text/plain'],
        ],
    })

    await send({
        'type': 'http.response.body',
        'body': b"Hello from ASGI!",
    })

# Test the ASGI app
responses = []
async def mock_send(message):
    responses.append(message)

async def main():
    await simple_asgi_app({'type': 'http'}, None, mock_send)
    print(f"Status: {responses[0]['status']}")
    print(responses[1]['body'].decode())

if __name__ == '__main__':
    asyncio.run(main())
uvicorn asgi_app:simple_asgi_app --lifespan off

When to Use Each

WSGI:

  • Simple request-response applications
  • Legacy systems integration
  • Applications built around synchronous libraries

ASGI:

  • Real-time applications
  • WebSocket requirements
  • High-concurrency needs
  • I/O-boundfluidattacks.github.ioaioextensions documentation, at its explanation of I/O-bound tasks and why asyncio speeds them up. applications

Performance Considerations

  • ASGI can overlap I/OInput/Output efficiently when the application uses nonblocking libraries.
  • Blocking calls inside an async handler block its event-loopA loop that waits for events, such as finished I/O, and runs the callback registered for each one. thread unless offloaded.
  • CPU-bound work needs an appropriate execution strategy with either interface, such as worker processes. Benchmark the actual application and server settings.

The following timeline is an idealized example with two synchronous WSGI workers, nonblocking database calls for ASGI, and no scheduling overhead; it is not a benchmark of the interfaces.

WSGI: gunicorn --workers=2R1 · worker 1R2 · worker 2R3 · worker 1180 msASGI: one uvicorn workerR1R2R3100 ms0306090120150180 msrunning Pythonwaiting on the databasequeued for a free worker
Three requests arrive at once. Each needs 5 ms of Python, an 80 ms database call, then 5 ms more. With two WSGI workers, the third request waits for a free worker and finishes at 180 ms. One ASGI worker overlaps all three database waits and finishes at 100 ms.