What’s new in Tornado 6.6.0

Oct 21, 2026

General changes

  • Python 3.15 is now supported. Python 3.9 and 3.10 are no longer supported; 3.11 is the minimum supported version.

  • Free-threaded python interpreters are now supported; prebuilt wheels are available for 3.14t and 3.15t.

  • Windows on ARM is now supported and prebuilt wheels are available.

  • Prebuilt wheels are now available for riscv64, ppc64le, and s390x architectures on Linux. These architectures are experimental and have limited support and testing.

  • PyPy 8.0 is now supported (for both Python 3.11 and 3.12).

  • Error messages have been improved in many places.

  • Improved handling of asyncio.CancelledError in various parts of the framework.

Feature highlights

  • New feature cross_origin_protection provides a new mechanism for protecting against XSRF and other cross-origin attacks. This feature is easier to use than xsrf_cookies and is expected to become the default in Tornado 6.7.

Deprecation notices

  • The host argument to the HTTPServerRequest constructor is deprecated (since Tornado 6.5.2); use headers["Host"] instead. It will be removed in Tornado 6.7.

  • The start_line argument to the HTTPServerRequest constructor will become required in Tornado 6.7. The method, uri, and version arguments are deprecated and will be removed at the same time. All remaining arguments will become keyword-only. A DeprecationWarning is now emitted when start_line is not provided or when the host, method, or uri arguments are used.

  • tornado.auth.OpenIdMixin is deprecated and will be removed in Tornado 6.7 (this is a change in the timeline; it was previously scheduled for removal in Tornado 7.0).

  • tornado.util.TimeoutError, tornado.gen.TimeoutError, and tornado.ioloop.TimeoutError are deprecated. They are aliases for the builtin TimeoutError, which should be used instead. (No warning is emitted when these aliases are used.)

  • Calling GzipDecompressor.decompress without a max_length argument (or with max_length=0) is deprecated and emits a DeprecationWarning. In Tornado 7.0, max_length will be required.

  • The xsrf_cookies application setting is expected to be deprecated in Tornado 6.7 (with warnings) in favor of cross_origin_protection. It will not be removed before Tornado 7.0.

Type annotation changes

  • tornado.gen.multi now has overload annotations to indicate that its return type depends on its inputs (list vs dict).

  • HTTPServerRequest.method and uri attributes are now typed as str instead of str | None.

tornado.curl_httpclient

  • The minimum supported version of libcurl is now 7.81 (which is the version shipped by Ubuntu 22.04 LTS).

  • Passing a coroutine as the streaming_callback argument now fails with a TypeError. (Coroutine streaming callbacks are only supported by simple_httpclient.)

tornado.gen

tornado.http1connection

  • Invalid Host headers are now logged at the INFO level instead of as an uncaught exception.

  • Line folding in HTTP headers is no longer supported. This was deprecated in Tornado 6.5 but is being removed sooner than originally scheduled to avoid DoS risk.

  • Reading a request or response body now yields to the event loop periodically, so a peer that sends a body faster than it can be processed (especially with many tiny chunks in chunked encoding) can no longer block the event loop for the duration of the body.

  • Small chunks of a body using chunked encoding are now coalesced before being passed to data_received, so the pieces seen by the application no longer correspond to the HTTP chunk boundaries.

  • A chunk in a chunked body that is not followed by \r\n is now reported as an HTTPInputError instead of raising AssertionError (or being silently accepted when Python is run with -O).

  • Gzipped bodies containing multiple members are now decompressed correctly. Trailing garbage at the end of a body or in between members is now an error instead of being silently ignored, as is a body that is truncated partway through a member. This applies both to responses received by simple_httpclient and to requests received by a server with decompress_request=True.

tornado.httputil

  • If start_line is not passed to HTTPServerRequest, missing method and uri arguments now default to GET and / (with a DeprecationWarning) instead of leaving these attributes as None.

  • HTTPHeaders.__delitem__ no longer raises KeyError at inappropriate times.

tornado.iostream

  • Fixed a socket leak when a timeout occurs during a TLS handshake, or when the connection of a SSLIOStream is cancelled.

  • Fixed errors relating to “existing exports of data” on PyPy 8.0.

  • Tests now pass when Python is built with AWS-LC or BoringCrypto instead of OpenSSL.

tornado.locale

  • format_date now correctly handles times in the future that are close to a multiple of 24 hours away.

tornado.locks

tornado.netutil

  • The default SSLContext objects are now lazily created on first use rather than at import time.

tornado.platform.asyncio

  • The selector thread used on Windows now cleans up its sockets correctly on interpreter exit, preventing logged warnings.

tornado.platform.caresresolver

  • The CaresResolver class (which has been deprecated since Tornado 6.2) requires pycares version 4.x; it will not be updated to support pycares 5.x.

tornado.queues

  • A timeout value of zero will either return or raise immediately. Previously, zero was treated as equivalent to None (wait forever). This affects Queue.get, Queue.put, and Queue.join.

tornado.simple_httpclient

  • Gzipped responses containing multiple members are now decompressed correctly, and malformed gzip data is now an error (see tornado.http1connection above).

  • Improved error messages for timeouts during the connection phase.

  • The streaming_callback argument to SimpleAsyncHTTPClient.fetch may now be a coroutine.

tornado.tcpclient

  • When a connection attempt is cancelled, sockets are now closed immediately instead of waiting for the connection to either complete or time out.

tornado.util

  • GzipDecompressor now decompresses streams containing multiple gzip members in full; previously everything after the first member was ignored. Data after the last member that is not the start of another member is now an error, as is a stream that ends partway through a member. GzipDecompressor.flush now raises zlib.error for truncated input and always returns an empty byte string (since decompress returns all of the output); it raises ValueError if unconsumed_tail has not been passed back to decompress.

tornado.web

  • New feature cross_origin_protection provides a new mechanism for protecting against XSRF and other cross-origin attacks. This feature is easier to use than xsrf_cookies and is expected to become the default in Tornado 6.7. Additional origins can be allowed with the new trusted_origins application setting or by overriding the new method RequestHandler.check_trusted_origin. See Cross-origin protection for details.

  • asyncio.CancelledError is now handled correctly instead of leaving connections to hang.

  • raise HTTPError(204) no longer attempts to incorrectly send a response body.

  • Writing a response body with a status code that does not allow one (such as 204 or 304) now raises RuntimeError instead of AssertionError (so the check is no longer skipped when Python runs with -O).

  • Signed values with valid signatures but certain formatting errors are now ignored instead of raising errors (consistent with historical behavior for other formatting errors).

  • The max_age argument to RequestHandler.set_cookie can no longer be abused for attribute injection.

  • RedirectHandler no longer allows values substituted into its url to change the scheme or netloc (user info, host, and port) of the redirect, which could be used as an open redirect. This includes protocol-relative URLs (//example.com) and variants that browsers treat the same way, such as /\example.com. Such requests now fail with a 400 error. As a consequence, substitutions can no longer be used in the scheme or netloc part of the url.

  • The check that prevents StaticFileHandler from redirecting to a protocol-relative URL (when default_filename is used) now also recognizes variants that use backslashes.

  • RequestHandler.static_url (and StaticFileHandler.get_version) now raise HTTPError (403) for paths outside of static_path instead of reading and hashing files elsewhere on the filesystem. Files that are not regular files (such as devices or FIFOs), and files larger than the new StaticFileHandler.MAX_VERSION_CONTENT_SIZE, get no version. Symlinks inside the static directory are still followed when computing versions; see StaticFileHandler for details. static_url is intended for an application’s known static files and should not be called with untrusted input.

  • The cache of static file versions is now limited to StaticFileHandler.MAX_CACHED_VERSIONS entries.

tornado.websocket

  • WebSocketHandler.check_origin is no longer called for requests that can be determined to be same-origin, using the Sec-Fetch-Site header or (if that header is absent) by comparing the Origin and Host headers. Its default implementation now calls RequestHandler.check_trusted_origin, so origins listed in the trusted_origins application setting are allowed. Applications that override check_origin to restrict origins further than the default will no longer see same-origin requests.

  • asyncio.CancelledError no longer breaks the websocket loop without closing the connection.

  • Receiving messages now yields to the event loop periodically, so a peer that sends many small messages faster than they can be processed can no longer block the event loop indefinitely.

  • When a ping is received while the pong for a previous ping has not yet been sent, only the most recent ping is answered (as permitted by RFC 6455 section 5.5.3). Previously, a peer that sent pings without reading the pongs could cause the server’s memory usage to grow without limit. on_ping is still called for every ping.

  • Versions 7 and 8 of the WebSocket protocol (pre-standard drafts from 2011) are no longer supported; only version 13 (RFC 6455) is accepted. The Sec-WebSocket-Origin header used by those versions is no longer consulted.