<span id="news-2026"></span>
# Changelog - 2026

## 25.3.0 - 2026-03-26

### Bug Fixes

- **HTTP/2 ASGI Body Duplication**: Fix request body being received twice in HTTP/2
  ASGI requests, causing JSON parsing errors with "Extra data" messages
  ([#3558](https://github.com/benoitc/gunicorn/issues/3558))

- **ASGI Chunked EOF Handling**: Add `finish()` method to callback parser to handle
  chunked encoding edge case where connection closes before final CRLF after zero-chunk

- **HTTP/2 Documentation**: Fix `http_protocols` examples to use comma-separated string
  instead of list syntax ([#3561](https://github.com/benoitc/gunicorn/issues/3561))

- **Chunked Encoding**: Reject chunk extensions containing bare CR bytes per RFC 9112
  ([#3556](https://github.com/benoitc/gunicorn/discussions/3556))

### Security

- **ASGI Parser Header Validation**: Add security checks per RFC 9110/9112:
  - Reject duplicate Content-Length headers
  - Reject requests with both Content-Length and Transfer-Encoding
  - Reject chunked transfer encoding in HTTP/1.0
  - Reject stacked chunked encoding
  - Validate Transfer-Encoding values
  - Strict chunk size validation

### Changes

- **Fast HTTP Parser**: Update to gunicorn_h1c >= 0.6.2 for `asgi_headers` property
  which provides headers with lowercase names directly from the C parser

- **ASGI PROXY Protocol**: Add PROXY protocol v1/v2 support to callback parser

---

## 25.2.0 - 2026-03-24

### New Features

- **Fast HTTP Parser (gunicorn_h1c 0.6.0)**: Integrate new exception types and limit
  parameters from gunicorn_h1c 0.6.0 for both WSGI and ASGI workers
  - Requires gunicorn_h1c >= 0.6.0 for `http_parser='fast'`
  - Falls back to Python parser in `auto` mode if version not met
  - Proper HTTP status codes for limit errors (414, 431)

### Bug Fixes

- **uWSGI Async Workers**: Fix `InvalidUWSGIHeader: incomplete header` error
  when using gevent or gthread workers with uwsgi protocol behind nginx.
  ([#3552](https://github.com/benoitc/gunicorn/issues/3552),
  [PR #3554](https://github.com/benoitc/gunicorn/pull/3554))

- **FileWrapper Iterator Protocol**: Add `__iter__` and `__next__` methods to
  `FileWrapper` for full PEP 3333 compliance. Previously only supported old-style
  `__getitem__` iteration which broke code explicitly using `iter()` or `next()`.
  ([#3396](https://github.com/benoitc/gunicorn/issues/3396),
  [PR #3550](https://github.com/benoitc/gunicorn/pull/3550))

### Performance

- **ASGI HTTP Parser Optimizations**: Improve ASGI worker HTTP parsing performance
  - Read chunks in 64-byte blocks instead of 1 byte at a time for chunk size lines and trailers
  - Reuse BytesIO buffers with truncate/seek instead of creating new objects (reduces GC pressure)
  - Use `bytearray.find()` directly instead of converting to bytes first
  - Use index-based iteration for header parsing instead of `list.pop(0)` (O(1) vs O(n))

---

## 25.1.0 - 2026-02-13

### New Features

- **Control Interface (gunicornc)**: Add interactive control interface for managing
  running Gunicorn instances, similar to birdc for BIRD routing daemon
  ([PR #3505](https://github.com/benoitc/gunicorn/pull/3505))
  - Unix socket-based communication with JSON protocol
  - Interactive mode with readline support and command history
  - Commands: `show all/workers/dirty/config/stats/listeners`
  - Worker management: `worker add/remove/kill`, `dirty add/remove`
  - Server control: `reload`, `reopen`, `shutdown`
  - New settings: `--control-socket`, `--control-socket-mode`, `--no-control-socket`
  - New CLI tool: `gunicornc` for connecting to control socket
  - See [Control Interface Guide](guides/gunicornc.md) for details

- **Dirty Stash**: Add global shared state between workers via `dirty.stash`
  ([PR #3503](https://github.com/benoitc/gunicorn/pull/3503))
  - In-memory key-value store accessible by all workers
  - Supports get, set, delete, clear, keys, and has operations
  - Useful for sharing state like feature flags, rate limits, or cached data

- **Dirty Binary Protocol**: Implement efficient binary protocol for dirty arbiter IPC
  using TLV (Type-Length-Value) encoding
  ([PR #3500](https://github.com/benoitc/gunicorn/pull/3500))
  - More efficient than JSON for binary data
  - Supports all Python types: str, bytes, int, float, bool, None, list, dict
  - Better performance for large payloads

- **Dirty TTIN/TTOU Signals**: Add dynamic worker scaling for dirty arbiters
  ([PR #3504](https://github.com/benoitc/gunicorn/pull/3504))
  - Send SIGTTIN to increase dirty workers
  - Send SIGTTOU to decrease dirty workers
  - Respects minimum worker constraints from app configurations

### Changes

- **ASGI Worker**: Promoted from beta to stable
- **Dirty Arbiters**: Now marked as beta feature

### Documentation

- Fix Markdown formatting in /configure documentation

---

## 25.0.3 - 2026-02-07

### Bug Fixes

- Fix RuntimeError when StopIteration is raised inside ASGI response body
  coroutine (PEP 479 compliance)

- Fix deprecation warning for passing maxsplit as positional argument in
  `re.split()` (Python 3.13+)

---

## 25.0.2 - 2026-02-06

### Bug Fixes

- Fix ASGI concurrent request failures through nginx proxy by normalizing
  sockaddr tuples to handle both 2-tuple (IPv4) and 4-tuple (IPv6) formats
  ([PR #3485](https://github.com/benoitc/gunicorn/pull/3485))

- Fix graceful disconnect handling for ASGI worker to properly handle
  client disconnects without raising exceptions
  ([PR #3485](https://github.com/benoitc/gunicorn/pull/3485))

- Fix lazy import of dirty module for gevent compatibility - prevents
  import errors when concurrent.futures is imported before gevent monkey-patching
  ([PR #3483](https://github.com/benoitc/gunicorn/pull/3483))

### Changes

- Refactor: Extract `_normalize_sockaddr` utility function for consistent
  socket address handling across workers

- Add license headers to all Python source files

- Update copyright year to 2026 in LICENSE and NOTICE files

---

## 25.0.1 - 2026-02-02

### Bug Fixes

- Fix ASGI streaming responses (SSE) hanging: add chunked transfer encoding for
  HTTP/1.1 responses without Content-Length header. Without chunked encoding,
  clients wait for connection close to determine end-of-response.

### Changes

- Update celery_alternative example to use FastAPI with native ASGI worker and
  uvloop for async task execution

### Testing

- Add ASGI compliance test suite with Docker-based integration tests covering HTTP,
  WebSocket, streaming, lifespan, framework integration (Starlette, FastAPI),
  HTTP/2, and concurrency scenarios

---

## 25.0.0 - 2026-02-01

### New Features

- **Dirty Arbiters**: Separate process pool for executing long-running, blocking
  operations (AI model loading, heavy computation) without blocking HTTP workers
  ([PR #3460](https://github.com/benoitc/gunicorn/pull/3460))
  - Inspired by Erlang's dirty schedulers
  - Asyncio-based with Unix socket IPC
  - Stateful workers that persist loaded resources
  - New settings: `--dirty-app`, `--dirty-workers`, `--dirty-timeout`,
    `--dirty-threads`, `--dirty-graceful-timeout`
  - Lifecycle hooks: `on_dirty_starting`, `dirty_post_fork`,
    `dirty_worker_init`, `dirty_worker_exit`

- **Per-App Worker Allocation for Dirty Arbiters**: Control how many dirty workers
  load each app for memory optimization with heavy models
  ([PR #3473](https://github.com/benoitc/gunicorn/pull/3473))
  - Set `workers` class attribute on DirtyApp (e.g., `workers = 2`)
  - Or use config format `module:class:N` (e.g., `myapp:HeavyModel:2`)
  - Requests automatically routed to workers with the target app
  - New exception `DirtyNoWorkersAvailableError` for graceful error handling
  - Example: 8 workers × 10GB model = 80GB → with `workers=2`: 20GB (75% savings)

- **HTTP/2 Support (Beta)**: Native HTTP/2 (RFC 7540) support for improved performance
  with modern clients ([PR #3468](https://github.com/benoitc/gunicorn/pull/3468))
  - Multiplexed streams over a single connection
  - Header compression (HPACK)
  - Flow control and stream prioritization
  - Works with gthread, gevent, and ASGI workers
  - New settings: `--http-protocols`, `--http2-max-concurrent-streams`,
    `--http2-initial-window-size`, `--http2-max-frame-size`, `--http2-max-header-list-size`
  - Requires SSL/TLS and h2 library: `pip install gunicorn[http2]`
  - See [HTTP/2 Guide](guides/http2.md) for details
  - New example: `examples/http2_gevent/` with Docker and tests

- **HTTP 103 Early Hints**: Support for RFC 8297 Early Hints to enable browsers to
  preload resources before the final response
  ([PR #3468](https://github.com/benoitc/gunicorn/pull/3468))
  - WSGI: `environ['wsgi.early_hints'](headers)` callback
  - ASGI: `http.response.informational` message type
  - Works with both HTTP/1.1 and HTTP/2

- **uWSGI Protocol for ASGI Worker**: The ASGI worker now supports receiving requests
  via the uWSGI binary protocol from nginx
  ([PR #3467](https://github.com/benoitc/gunicorn/pull/3467))

### Bug Fixes

- Fix HTTP/2 ALPN negotiation for gevent and eventlet workers when
  `do_handshake_on_connect` is False (the default). The TLS handshake is now
  explicitly performed before checking `selected_alpn_protocol()`.

- Fix setproctitle initialization with systemd socket activation
  ([#3465](https://github.com/benoitc/gunicorn/issues/3465))

- Fix `Expect: 100-continue` handling: ignore the header for HTTP/1.0 requests
  since 100-continue is only valid for HTTP/1.1+
  ([PR #3463](https://github.com/benoitc/gunicorn/pull/3463))

- Fix missing `_expected_100_continue` attribute in UWSGIRequest

- Disable setproctitle on macOS to prevent segfaults during process title updates

- Publish full exception traceback when the application fails to load
  ([#3462](https://github.com/benoitc/gunicorn/issues/3462))

### Deprecations

- **Eventlet Worker**: The `eventlet` worker is deprecated and will be removed in
  Gunicorn 26.0. Eventlet itself is [no longer actively maintained](https://eventlet.readthedocs.io/en/latest/asyncio/migration.html).
  Please migrate to `gevent`, `gthread`, or another supported worker type.

### Changes

- Remove obsolete Makefile targets
  ([PR #3471](https://github.com/benoitc/gunicorn/pull/3471))

---

## 24.1.1 - 2026-01-24

### Bug Fixes

- Fix `forwarded_allow_ips` and `proxy_allow_ips` to remain as strings for backward
  compatibility with external tools like uvicorn. Network validation now uses strict
  mode to detect invalid CIDR notation (e.g., `192.168.1.1/24` where host bits are set)
  ([#3458](https://github.com/benoitc/gunicorn/issues/3458),
  [PR #3459](https://github.com/benoitc/gunicorn/pull/3459))

---

## 24.1.0 - 2026-01-23

### New Features

- **Official Docker Image**: Gunicorn now publishes official Docker images to GitHub
  Container Registry at `ghcr.io/benoitc/gunicorn`
  - Based on Python 3.12 slim image
  - Uses recommended worker formula (2 × CPU + 1)
  - Configurable via environment variables

- **PROXY Protocol v2 Support**: Extended PROXY protocol implementation to support
  the binary v2 format in addition to the existing text-based v1 format
  ([PR #3451](https://github.com/benoitc/gunicorn/pull/3451))
  - New `--proxy-protocol` modes: `off`, `v1`, `v2`, `auto`
  - `auto` mode (default when enabled) detects v1 or v2 automatically
  - v2 binary format is more efficient and supports additional metadata
  - Works with HAProxy, AWS NLB/ALB, and other PROXY protocol v2 sources

- **CIDR Network Support**: `--forwarded-allow-ips` and `--proxy-allow-from` now
  accept CIDR notation (e.g., `192.168.0.0/16`) for specifying trusted networks
  ([PR #3449](https://github.com/benoitc/gunicorn/pull/3449))

- **Socket Backlog Metric**: New `gunicorn.socket.backlog` gauge metric reports
  the current socket backlog size on Linux systems
  ([PR #3450](https://github.com/benoitc/gunicorn/pull/3450))

- **InotifyReloader Enhancement**: The inotify-based reloader now watches newly
  imported modules, not just those loaded at startup
  ([PR #3447](https://github.com/benoitc/gunicorn/pull/3447))

### Bug Fixes

- Fix signal handling regression where SIGCLD alias caused "Unhandled signal: cld"
  errors on Linux when workers fail during boot
  ([#3453](https://github.com/benoitc/gunicorn/discussions/3453))

- Fix socket blocking mode on keepalive connections preventing SSL handshake
  failures with async workers
  ([PR #3452](https://github.com/benoitc/gunicorn/pull/3452))

- Use smaller buffer size in `finish_body()` for faster timeout detection on
  slow or abandoned connections
  ([PR #3453](https://github.com/benoitc/gunicorn/pull/3453))

- Handle `SSLWantReadError` in `finish_body()` to prevent worker hangs during
  SSL renegotiation
  ([PR #3448](https://github.com/benoitc/gunicorn/pull/3448))

- Log SIGTERM as info level instead of warning to reduce noise in orchestrated
  environments
  ([PR #3446](https://github.com/benoitc/gunicorn/pull/3446))

- Print exception details to stderr when worker fails to boot
  ([PR #3443](https://github.com/benoitc/gunicorn/pull/3443))

- Fix `unreader.unread()` to prepend data to buffer instead of appending
  ([PR #3442](https://github.com/benoitc/gunicorn/pull/3442))

- Prevent `RecursionError` when pickling Config objects
  ([PR #3441](https://github.com/benoitc/gunicorn/pull/3441))

- Use proper exception chaining with `raise from` in glogging.py
  ([PR #3440](https://github.com/benoitc/gunicorn/pull/3440))

---

## 24.0.0 - 2026-01-23

### New Features

- **ASGI Worker (Beta)**: Native asyncio-based ASGI support for running async Python
  frameworks like FastAPI, Starlette, and Quart without external dependencies
  ([PR #3444](https://github.com/benoitc/gunicorn/pull/3444))
  - HTTP/1.1 with keepalive connections
  - WebSocket support
  - Lifespan protocol for startup/shutdown hooks
  - Optional uvloop for improved performance
  - New settings: `--asgi-loop`, `--asgi-lifespan`, `--root-path`

- **uWSGI Binary Protocol**: Support for receiving requests from nginx via
  `uwsgi_pass` directive, enabling efficient binary protocol communication
  ([PR #3444](https://github.com/benoitc/gunicorn/pull/3444))
  - New settings: `--protocol uwsgi`, `--uwsgi-allow-from`

- **Documentation Migration**: Migrated documentation from Sphinx to MkDocs
  with Material theme for improved navigation and mobile experience
  ([PR #3426](https://github.com/benoitc/gunicorn/pull/3426))

### Security

- **eventlet**: Require eventlet >= 0.40.3 to address CVE-2021-21419 (websocket
  memory exhaustion) and CVE-2025-58068 (HTTP request smuggling)
  ([PR #3445](https://github.com/benoitc/gunicorn/pull/3445))

- **gevent**: Require gevent >= 24.10.1 to address CVE-2023-41419 (HTTP request
  smuggling) and CVE-2024-3219 (socket.socketpair vulnerability)
  ([PR #3445](https://github.com/benoitc/gunicorn/pull/3445))

- **tornado**: Require tornado >= 6.5.0 to address CVE-2025-47287 (HTTP request
  smuggling) and other security fixes
  ([PR #3445](https://github.com/benoitc/gunicorn/pull/3445))

### Changes

- Documentation now hosted at https://gunicorn.org
- Updated license configuration to PEP 639 format for uv compatibility

!!! warning "ASGI Worker Beta"
    The ASGI worker is a beta feature. While tested, the API and behavior
    may change in future releases. Please report any issues on GitHub.
