Of course! Let's break down the environ dictionary in Python's WSGI (Web Server Gateway Interface). It's one of the most fundamental concepts to understand when working with WSGI.

What is environ?
In simple terms, environ is a dictionary that contains all the information about a single web request.
Think of it as the "request object" or the "context" for the current HTTP request being handled by your WSGI application. The web server (like Gunicorn, uWSGI, or the built-in one in Flask/Django) creates this dictionary and passes it to your application as the first argument.
The WSGI specification (PEP 3333) defines the exact keys and values that must be present in this dictionary, ensuring that any WSGI-compliant server can communicate with any WSGI-compliant application.
The Big Picture: WSGI Flow
To understand environ, it helps to see where it fits in the WSGI architecture:

+-------------------+ +-----------------------+ +------------------+
| Web Browser | | Web Server | | Your App |
| (Client) | | (e.g., Gunicorn, Nginx)| | (e.g., Flask, |
| | | | | Django, your |
| 1. Sends HTTP | | | | own code) |
| Request | | 1. Receives Request | | |
| |----->| 2. Creates `environ` |----->| 1. Receives |
| | | & `start_response` | | `environ` dict|
| | | 3. Calls your app | | & function |
| |<-----| with them |<-----| 2. Processes |
| 2. Receives HTTP | | 4. Streams Response | | request |
| Response | | | | 3. Returns |
| | | | | response |
+-------------------+ +-----------------------+ | iterable |
| 4. Server sends |
| response to |
| client |
+------------------+
- The web server receives an HTTP request.
- It parses the request and populates a dictionary called
environwith all the request's details (headers, path, method, etc.). - It also creates a function called
start_responsewhich your application will use to start the HTTP response. - The server calls your WSGI application, passing it the
environdictionary and thestart_responsefunction. - Your application uses the data in
environto figure out what to do, and usesstart_responseto set the status code and headers of the response. - Your application returns an iterable (like a list of strings) containing the body of the response.
- The server takes this iterable and sends it back to the client as the HTTP response.
The Structure of the environ Dictionary
The environ dictionary is divided into several categories of variables. Here are the most important ones:
Request-Specific Variables (Required by WSGI)
These are the core details of the HTTP request.
| Key | Example Value | Description |
|---|---|---|
REQUEST_METHOD |
'GET', 'POST' |
The HTTP method (verb) used. |
SCRIPT_NAME |
or '/myapp' |
The initial portion of the URL's path that corresponds to the application itself. Used for URL routing. |
PATH_INFO |
'/hello' |
The rest of the URL's path, pointing to a specific resource within the application. |
QUERY_STRING |
'name=John&age=30' |
The part of the URL after the , containing query parameters. |
SERVER_NAME |
'localhost' |
The hostname of the server. |
SERVER_PORT |
'8000' |
The port number the server is listening on. |
SERVER_PROTOCOL |
'HTTP/1.1' |
The HTTP protocol version used by the client. |
CONTENT_TYPE |
'application/json' |
The MIME type of the request body (if any). |
CONTENT_LENGTH |
'27' |
The length of the request body in bytes (if any). |
wsgi.url_scheme |
'http' |
The URL scheme used (http or https). |
CGI Variables (Legacy but Standard)
These variables originate from the older Common Gateway Interface (CGI) standard and provide additional environment information.
| Key | Example Value | Description |
|---|---|---|
REMOTE_ADDR |
'192.168.1.101' |
The IP address of the client. |
REMOTE_USER |
None or 'jane_doe' |
The authenticated username (if authentication is used). |
SERVER_SOFTWARE |
'gunicorn/20.1.0' |
The name and version of the web server software. |
WSGI-Specific Variables
These variables are defined by the WSGI specification itself to help the application and server communicate.

| Key | Example Value | Description |
|---|---|---|
wsgi.version |
(1, 0) |
A tuple specifying the WSGI version the server is using. |
wsgi.url_scheme |
'http' |
The URL scheme (http or https). |
wsgi.input |
An input stream (with a read() method) for reading the request body. |
|
wsgi.errors |
An error stream (with a write() method) for writing log messages. |
|
wsgi.multithread |
True |
A boolean indicating if the application may be invoked concurrently by multiple threads. |
wsgi.multiprocess |
True |
A boolean indicating if the application may be invoked concurrently by multiple processes. |
wsgi.run_once |
False |
A boolean indicating if the server expects the application to be executed only this one time. |
HTTP Headers
All HTTP headers sent by the client are included in environ with a specific naming convention:
- The header name is converted to uppercase.
- Hyphens () are replaced by underscores (
_). - The
HTTP_prefix is added.
| Original Header | environ Key |
Example Value |
|---|---|---|
Accept |
HTTP_ACCEPT |
'text/html, application/xhtml+xml, ...' |
User-Agent |
HTTP_USER_AGENT |
'Mozilla/5.0 (Macintosh; ...)' |
Cookie |
HTTP_COOKIE |
'session_id=abc123; user_prefs=dark_mode' |
Host |
HTTP_HOST |
'localhost:8000' |
A Concrete Example: A "Raw" WSGI Application
Let's see how to use environ by creating a minimal WSGI application without any framework like Flask. This will make the concepts crystal clear.
Save this code as myapp.py:
# myapp.py
def simple_wsgi_app(environ, start_response):
"""
A very simple WSGI application.
environ: A dictionary containing all request information.
start_response: A function to begin the HTTP response.
"""
# 1. Use the environ dictionary to get request details
request_method = environ['REQUEST_METHOD']
path_info = environ['PATH_INFO']
query_string = environ.get('QUERY_STRING', '')
# You can access headers like this
user_agent = environ.get('HTTP_USER_AGENT', 'Unknown')
# 2. Prepare the response content
status = '200 OK'
headers = [
('Content-Type', 'text/plain; charset=utf-8'),
('X-Custom-Header', 'This is a custom header!')
]
# 3. Call start_response to send the status and headers
# This does not send the response body, it just sends the headers.
start_response(status, headers)
# 4. Create the response body (must be an iterable of bytes)
response_body = [
f"Request Method: {request_method}\n".encode('utf-8'),
f"Path Info: {path_info}\n".encode('utf-8'),
f"Query String: {query_string}\n".encode('utf-8'),
f"User Agent: {user_agent}\n".encode('utf-8'),
"\n--- Full environ dictionary (truncated) ---\n".encode('utf-8'),
]
# Add a snippet of the environ dict for demonstration
# We don't want to print the whole thing, it's huge!
snippet = "\n".join([f" {k}: {v}" for k, v in list(environ.items())[:10]])
response_body.append(snippet.encode('utf-8'))
return response_body
# This is how you would run this app with Python's built-in server
# (for real applications, use a proper server like Gunicorn)
if __name__ == '__main__':
from wsgiref.simple_server import make_server
# Create a server on port 8000, using our app
httpd = make_server('', 8000, simple_wsgi_app)
print("Serving on port 8000...")
httpd.serve_forever()
How to Run It:
- Save the code as
myapp.py. - Open your terminal and run it:
python myapp.py
- Open your web browser and go to:
http://localhost:8000/http://localhost:8000/hello?name=Worldhttp://localhost:8000/post-test(and try to send a POST request using a tool like Postman orcurl)
Example Output for http://localhost:8000/hello?name=World:
You'll see something like this in your browser:
Request Method: GET
Path Info: /hello
Query String: name=World
User Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...
--- Full environ dictionary (truncated) ---
REQUEST_METHOD: GET
SCRIPT_NAME:
PATH_INFO: /hello
QUERY_STRING: name=World
SERVER_PROTOCOL: HTTP/1.1
QUERY_STRING: name=World
HTTP_HOST: localhost:8000
HTTP_USER_AGENT: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...
HTTP_ACCEPT: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
HTTP_ACCEPT_LANGUAGE: en-US,en;q=0.5
HTTP_ACCEPT_ENCODING: gzip, deflate
...
Summary
environis the heart of a WSGI request. It's a dictionary that acts as a snapshot of everything the web server knows about the incoming request.- It contains request details (method, path, headers), server information (name, port), and WSGI-specific control variables.
- When you use a web framework like Flask or Django, you rarely see the raw
environdictionary. The framework parses it for you and provides friendly objects likerequest.path,request.args, orrequest.headersto make your life easier. - Understanding
environis crucial if you ever need to write a custom WSGI middleware, integrate with an unusual web server, or debug deep-level request handling issues.
