Skip to content

Changelog

26.0.0 - 2026-05-05

Breaking Changes

  • Eventlet worker removed: The eventlet worker class has been dropped. Migrate to gevent, gthread, or tornado.

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-form request-target outside CONNECT
  • Reject asterisk-form request-target outside OPTIONS
  • Reject relative-reference request-targets

  • 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-Length list form (RFC 9112 section 6.3)

  • Request Smuggling Hardening:

  • Tighten keepalive gate and scope finish_body byte cap
  • Keep _body_receiver alive 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

  • PROXY Protocol (ASGI): Enforce proxy_allow_ips and 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-Length on 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

  • HTTP/2 ASGI:

  • Fix _handle_stream_ended to set _body_complete in the async HTTP/2 handler so request bodies finalize correctly on stream end
  • Add InvalidChunkExtension mapping and fast-parser support in ASGI tests (#3565)

  • HTTP/1.1 100-Continue: Stop adding Transfer-Encoding: chunked to 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 text key is None

  • Early Hints: Validate headers in the early_hints callback to match process_headers; pass only the header name to InvalidHeader (#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-Encoding header for BlackSheep streaming

Refactoring

  • Split BodyReceiver._closed into separate transport and body-wait flags for clearer keepalive/EOF semantics.

Changes

  • Fast HTTP Parser: Require gunicorn_h1c >= 0.6.5. Drop the last python_only test markers; the C extension is now used wherever available (CPython only; PyPy continues to use the Python parser).

  • Test Dependencies: Add h2 and uvloop to the testing extra; remove eventlet.

  • Docker Build: Bump GitHub Actions docker/setup-qemu-action, docker/setup-buildx-action, docker/login-action, docker/build-push-action, and docker/metadata-action to current major versions.


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)

  • 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)

  • Chunked Encoding: Reject chunk extensions containing bare CR bytes per RFC 9112 (#3556)

  • Request Line Limit: Fix --limit-request-line 0 to 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_headers property and InvalidChunkExtension validation for bare CR rejection

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

  • Docker Images: Update to Python 3.14


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, PR #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, PR #3550)

Performance

  • ASGI HTTP Parser Optimizations: Improve ASGI worker HTTP parsing performance
  • Callback-based parsing with direct bytearray buffer 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))

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)
  • 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 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 - 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)

  • Fix graceful disconnect handling for ASGI worker to properly handle client disconnects without raising exceptions (PR #3485)

  • Fix lazy import of dirty module for gevent compatibility - prevents import errors when concurrent.futures is imported before gevent monkey-patching (PR #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)
  • 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 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)

  • 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 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)

  • 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)

Bug Fixes

  • Fix HTTP/2 ALPN negotiation for the gevent worker 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)

  • 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)

  • 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)

  • Fix ASGI: quick shutdown on SIGINT/SIGQUIT, graceful on SIGTERM

Removals

  • Eventlet Worker: The eventlet worker has been removed. Eventlet itself is no longer actively maintained; the worker was deprecated in 25.x and is now gone. Migrate to gevent, gthread, or one of the ASGI workers.

Changes

  • Remove obsolete Makefile targets (PR #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, PR #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

  • New --proxy-protocol modes: off, v1, v2, auto
  • 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

  • Socket Backlog Metric: New gunicorn.socket.backlog gauge metric reports the current socket backlog size on Linux systems

  • InotifyReloader Enhancement: The inotify-based reloader now watches newly imported modules, not just those loaded at startup

Bug Fixes

  • Fix signal handling regression where SIGCLD alias caused errors on Linux
  • Fix socket blocking mode on keepalive connections with async workers
  • Handle SSLWantReadError in finish_body() to prevent worker hangs
  • Log SIGTERM as info level instead of warning
  • Print exception details to stderr when worker fails to boot
  • Fix unreader.unread() to prepend data to buffer instead of appending
  • Prevent RecursionError when pickling Config objects

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
  • HTTP/1.1 with keepalive connections
  • WebSocket support
  • Lifespan protocol for startup/shutdown hooks
  • Optional uvloop for improved performance

  • uWSGI Binary Protocol: Support for receiving requests from nginx via uwsgi_pass directive

  • Documentation Migration: Migrated to MkDocs with Material theme

Security

  • gevent: Require gevent >= 24.10.1 (CVE-2023-41419, CVE-2024-3219)
  • tornado: Require tornado >= 6.5.0 (CVE-2025-47287)

23.0.0 - 2024-08-10

  • minor docs fixes (PR #3217, PR #3089, PR #3167)
  • worker_class parameter accepts a class (PR #3079)
  • fix deadlock if request terminated during chunked parsing (PR #2688)
  • permit receiving Transfer-Encodings: compress, deflate, gzip (PR #3261)
  • permit Transfer-Encoding headers specifying multiple encodings. note: no parameters, still (PR #3261)
  • sdist generation now explicitly excludes sphinx build folder (PR #3257)
  • decode bytes-typed status (as can be passed by gevent) as utf-8 instead of raising TypeError (PR #2336)
  • raise correct Exception when encounting invalid chunked requests (PR #3258)
  • the SCRIPT_NAME and PATH_INFO headers, when received from allowed forwarders, are no longer restricted for containing an underscore (PR #3192)
  • include IPv6 loopback address [::1] in default for forwarded-allow-ips and proxy-allow-ips (PR #3192)

Note

  • The SCRIPT_NAME change mitigates a regression that appeared first in the 22.0.0 release
  • Review your forwarded-allow-ips setting if you are still not seeing the SCRIPT_NAME transmitted
  • Review your forwarder-headers setting if you are missing headers after upgrading from a version prior to 22.0.0

Breaking changes

  • refuse requests where the uri field is empty (PR #3255)
  • refuse requests with invalid CR/LR/NUL in heade field values (PR #3253)
  • remove temporary --tolerate-dangerous-framing switch from 22.0 (PR #3260)
  • If any of the breaking changes affect you, be aware that now refused requests can post a security problem, especially so in setups involving request pipe-lining and/or proxies.

22.0.0 - 2024-04-17

  • use utime to notify workers liveness
  • migrate setup to pyproject.toml
  • fix numerous security vulnerabilities in HTTP parser (closing some request smuggling vectors)
  • parsing additional requests is no longer attempted past unsupported request framing
  • on HTTP versions < 1.1 support for chunked transfer is refused (only used in exploits)
  • requests conflicting configured or passed SCRIPT_NAME now produce a verbose error
  • Trailer fields are no longer inspected for headers indicating secure scheme
  • support Python 3.12

Breaking changes

  • minimum version is Python 3.7
  • the limitations on valid characters in the HTTP method have been bounded to Internet Standards
  • requests specifying unsupported transfer coding (order.md) are refused by default (rare.md)
  • HTTP methods are no longer casefolded by default (IANA method registry contains none affected)
  • HTTP methods containing the number sign (#) are no longer accepted by default (rare.md)
  • HTTP versions < 1.0 or >= 2.0 are no longer accepted by default (rare, only HTTP/1.1 is supported)
  • HTTP versions consisting of multiple digits or containing a prefix/suffix are no longer accepted
  • HTTP header field names Gunicorn cannot safely map to variables are silently dropped, as in other software
  • HTTP headers with empty field name are refused by default (no legitimate use cases, used in exploits)
  • requests with both Transfer-Encoding and Content-Length are refused by default (such a message might indicate an attempt to perform request smuggling)
  • empty transfer codings are no longer permitted (reportedly seen with really old & broken proxies)

Security

  • fix CVE-2024-1135

History