Releases: benoitc/gunicorn
26.0.0
Breaking Changes
- Eventlet worker removed: The
eventletworker class has been dropped. Migrate togevent,gthread, ortornado.
New Features
- ASGI Framework Compatibility Suite: New end-to-end compatibility test harness covering Starlette, FastAPI, Litestar, Quart, Sanic, and BlackSheep. Current grid passes 438/444 tests (98%).
- ASGI Test Suite Expansion: 134 additional ASGI unit tests covering protocol semantics, lifespan, websockets, and chunked framing.
Security
- HTTP/1.1 Request-Target Validation (RFC 9112 sections 3.2.3, 3.2.4):
- Reject
authority-formrequest-target outsideCONNECT - Reject
asterisk-formrequest-target outsideOPTIONS - Reject
relative-referencerequest-targets
- Reject
- Header Field Hardening (RFC 9110):
- Reject control characters in header field-value (section 5.5)
- Reject forbidden trailer field-names (section 6.5.1)
- Reject
Content-Lengthlist form (RFC 9112 section 6.3)
- Request Smuggling Hardening:
- Tighten keepalive gate and scope
finish_bodybyte cap - Keep
_body_receiveralive across the keepalive smuggling gate so pipelined requests cannot re-enter a closed body - Address parser/protocol findings from a six-point WSGI/ASGI audit
- Tighten keepalive gate and scope
- PROXY Protocol (ASGI): Enforce
proxy_allow_ipsand tighten v1/v2 parsing in the ASGI callback parser. - Connection Draining: Drain the connection on close per RFC 9112 section 9.6 to prevent reset-on-close truncation.
Bug Fixes
- Body Framing on HEAD/204/304:
- Keep
Content-Lengthon HEAD and 304 responses (#3621) - Drop body framing on HEAD/204/304 even when the framework set it
- Warn once when an ASGI app emits a body for a no-body response
- Keep
- HTTP/2 ASGI:
- Fix
_handle_stream_endedto set_body_completein the async HTTP/2 handler so request bodies finalize correctly on stream end - Add
InvalidChunkExtensionmapping and fast-parser support in ASGI tests (#3565)
- Fix
- HTTP/1.1 100-Continue: Stop adding
Transfer-Encoding: chunkedto 100-Continue interim responses. - WebSocket Close Handshake (RFC 6455):
- Comply with the close handshake state machine
- Close the transport after the close handshake completes
- Fix binary send when the
textkey isNone
- Early Hints: Validate headers in the
early_hintscallback to matchprocess_headers; pass only the header name toInvalidHeader(#3588). - ASGI Framework Fixes:
- Fix ASGI disconnect handling for Django-style apps
- Fix Litestar request handling (use raw ASGI receive for body/headers)
- Fix Litestar HTTP endpoints for compatibility tests
- Fix Quart headers endpoint to normalize keys to lowercase
- Fix Quart WebSocket close test app (missing
accept()) - Fix duplicate
Transfer-Encodingheader for BlackSheep streaming
Refactoring
- Split
BodyReceiver._closedinto separate transport and body-wait flags for clearer keepalive/EOF semantics.
Changes
- Fast HTTP Parser: Require
gunicorn_h1c >= 0.6.5. Drop the lastpython_onlytest markers; the C extension is now used wherever available (CPython only; PyPy continues to use the Python parser). - Test Dependencies: Add
h2anduvloopto thetestingextra; removeeventlet. - Docker Build: Bump GitHub Actions
docker/setup-qemu-action,docker/setup-buildx-action,docker/login-action,docker/build-push-action, anddocker/metadata-actionto current major versions.
Full changelog: 25.3.0...26.0.0
Gunicorn 25.3.0
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) -
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_protocolsexamples to use comma-separated string
instead of list syntax (#3561) -
Chunked Encoding: Reject chunk extensions containing bare CR bytes per RFC 9112
(#3556) -
Request Line Limit: Fix
--limit-request-line 0to mean unlimited as documented,
instead of using default maximum. Works with both Python and fast C parser.
(#3563)
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.3 for
asgi_headersproperty
andInvalidChunkExtensionvalidation for bare CR rejection -
ASGI PROXY Protocol: Add PROXY protocol v1/v2 support to callback parser
-
Docker Images: Update to Python 3.14
Gunicorn 25.2.0
New Features
- Fast HTTP Parser (gunicorn_h1c 0.4.1): Integrate new exception types and limit parameters from gunicorn_h1c 0.4.1 for both WSGI and ASGI workers
- Requires gunicorn_h1c >= 0.4.1 for
http_parser='fast' - Falls back to Python parser in
automode if version not met - Proper HTTP status codes for limit errors (414, 431)
- Requires gunicorn_h1c >= 0.4.1 for
Bug Fixes
-
uWSGI Async Workers: Fix
InvalidUWSGIHeader: incomplete headererror when using gevent or gthread workers with uwsgi protocol behind nginx. (#3552, PR #3554) -
FileWrapper Iterator Protocol: Add
__iter__and__next__methods toFileWrapperfor full PEP 3333 compliance. (#3396, PR #3550)
Performance
- ASGI HTTP Parser Optimizations: Improve ASGI worker HTTP parsing performance
- Callback-based parsing with direct
bytearraybuffer operations - 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))
- Callback-based parsing with direct
Gunicorn 25.1.0
New Features
-
Control Interface (gunicornc): Add interactive control interface for managing
running Gunicorn instances, similar to birdc for BIRD routing daemon
(PR #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:
gunicorncfor connecting to control socket - See Control Interface Guide for details
-
Dirty Stash: Add global shared state between workers via
dirty.stash
(PR #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)- 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)- 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
What's Changed
Bug Fixes
- Fix RuntimeError when StopIteration raised in ASGI coroutine (#3484)
- Fix passing maxsplit in re.split() as positional argument (deprecated in Python 3.13)
Documentation
- Updated sponsorship section and homepage
Full Changelog: 25.0.2...25.0.3
25.0.2
What's Changed
Bug Fixes
- Fix ASGI concurrent request failures through nginx proxy
- Graceful disconnect handling for ASGI worker
- Lazy import dirty module for gevent compatibility
Other
- Increase CI timeout for signal tests on PyPy
- Remove trailing blank line in instrument/init.py
Full Changelog: 25.0.1...25.0.2
25.0.1
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
Gunicorn 25.0.0
New Features
-
Dirty Arbiters: Separate process pool for executing long-running, blocking
operations (AI model loading, heavy computation) without blocking HTTP workers
(PR #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)- Set
workersclass 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
DirtyNoWorkersAvailableErrorfor graceful error handling - Example: 8 workers × 10GB model = 80GB → with
workers=2: 20GB (75% savings)
- Set
-
HTTP/2 Support (Beta): Native HTTP/2 (RFC 7540) support for improved performance
with modern clients (PR #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] - 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)- WSGI:
environ['wsgi.early_hints'](headers)callback - ASGI:
http.response.informationalmessage type - Works with both HTTP/1.1 and HTTP/2
- WSGI:
-
uWSGI Protocol for ASGI Worker: The ASGI worker now supports receiving requests
via the uWSGI binary protocol from nginx
(PR #3467)
Bug Fixes
-
Fix HTTP/2 ALPN negotiation for gevent and eventlet workers when
do_handshake_on_connectis False (the default). The TLS handshake is now
explicitly performed before checkingselected_alpn_protocol(). -
Fix setproctitle initialization with systemd socket activation
(#3465) -
Fix
Expect: 100-continuehandling: ignore the header for HTTP/1.0 requests
since 100-continue is only valid for HTTP/1.1+
(PR #3463) -
Fix missing
_expected_100_continueattribute in UWSGIRequest -
Disable setproctitle on macOS to prevent segfaults during process title updates
-
Publish full exception traceback when the application fails to load
(#3462) -
Fix ASGI: quick shutdown on SIGINT/SIGQUIT, graceful on SIGTERM
Deprecations
- Eventlet Worker: The
eventletworker is deprecated and will be removed in
Gunicorn 26.0. Eventlet itself is no longer actively maintained.
Please migrate togevent,gthread, or another supported worker type.
Changes
- Remove obsolete Makefile targets
(PR #3471) - Replace RST with markdown documentation format
24.1.1
Bug Fixes
- Fix
forwarded_allow_ipsandproxy_allow_ipsto 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/24where host bits are set)
(#3458,
PR #3459)
Full Changelog: 24.1.0...24.1.1
Gunicorn 24.1.0
New Features
-
Official Docker Image: Gunicorn now publishes official Docker images to GitHub Container Registry (PR #3454)
- Available at
ghcr.io/benoitc/gunicorn - Based on Python 3.12 slim image
- Uses recommended worker formula (2 × CPU + 1)
- Configurable via environment variables
- Available at
-
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)
- New
--proxy-protocolmodes:off,v1,v2,auto automode (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
- New
-
CIDR Network Support:
--forwarded-allow-ipsand--proxy-allow-fromnow accept CIDR notation (e.g.,192.168.0.0/16) for specifying trusted networks (PR #3449) -
Socket Backlog Metric: New
gunicorn.socket.backloggauge metric reports the current socket backlog size on Linux systems (PR #3450) -
InotifyReloader Enhancement: The inotify-based reloader now watches newly imported modules, not just those loaded at startup (PR #3447)
Bug Fixes
- Fix signal handling regression where SIGCLD alias caused "Unhandled signal: cld" errors on Linux when workers fail during boot (#3453)
- Fix socket blocking mode on keepalive connections preventing SSL handshake failures with async workers (PR #3452)
- Use smaller buffer size in
finish_body()for faster timeout detection on slow or abandoned connections (PR #3453) - Handle
SSLWantReadErrorinfinish_body()to prevent worker hangs during SSL renegotiation (PR #3448) - Log SIGTERM as info level instead of warning to reduce noise in orchestrated environments (PR #3446)
- Print exception details to stderr when worker fails to boot (PR #3443)
- Fix
unreader.unread()to prepend data to buffer instead of appending (PR #3442) - Prevent
RecursionErrorwhen pickling Config objects (PR #3441) - Use proper exception chaining with
raise fromin glogging.py (PR #3440)
Installation
pip install gunicorn==24.1.0Or use the official Docker image:
docker pull ghcr.io/benoitc/gunicorn:24.1.0