Compatibility dates
Cloudflare regularly updates the Workers runtime. These updates apply to all Workers globally and should never cause a Worker that is already deployed to stop functioning. Sometimes, though, some changes may be backwards-incompatible. In particular, there might be bugs in the runtime API that existing Workers may inadvertently depend upon. Cloudflare implements bug fixes that new Workers can opt into while existing Workers will continue to see the buggy behavior to prevent breaking deployed Workers.
The compatibility date (and flags) are how you, as a developer, opt into these runtime changes. Compatibility flags will often have a date in which they are enabled by default, and so, by specifying a compatibility_date
for your Worker, you can quickly enable all of these various compatibility flags up to, and including, that date.
When you start your project, you should always set compatibility_date
to the current date. You should occasionally update the compatibility_date
field. When updating, you should refer to this page to find out what has changed, and you should be careful to test your Worker to see if the changes affect you, updating your code as necessary. The new compatibility date takes effect when you next run the npx wrangler deploy
command.
There is no need to update your compatibility_date
if you do not want to. The Workers runtime will support old compatibility dates forever. If, for some reason, Cloudflare finds it is necessary to make a change that will break live Workers, Cloudflare will actively contact affected developers. That said, Cloudflare aims to avoid this if at all possible.
However, even though you do not need to update the compatibility_date
field, it is a good practice to do so for two reasons:
- Sometimes, new features can only be made available to Workers that have a current
compatibility_date
. To access the latest features, you need to stay up-to-date. - Generally, other than this page, the Workers documentation may only describe the current
compatibility_date
, omitting information about historical behavior. If your Worker uses an oldcompatibility_date
, you will need to continuously refer to this page in order to check if any of the APIs you are using have changed.
The compatibility date can be set in a Worker’s wrangler.toml
file.
When a Worker is created through the Cloudflare Dashboard, the compatibility date is automatically set to the current date.
The compatibility date cannot be updated on the Cloudflare Dashboard at this time.
The compatibility date can be set when uploading a Worker using the Workers Script API or Workers Versions API in the request body’s metadata
field.
If a compatibility date is not specified on upload via the API, it defaults to the oldest compatibility date, before any flags took effect (2021-11-02). When creating new Workers, it is highly recommended to set the compatibility date to the current date when uploading via the API.
In addition to setting a compatibility_date
, you may also provide a list of compatibility_flags
, which enable or disable specific changes.
Most developers will not need to use compatibility_flags
. compatibility_flags
can be useful if you want to help the Workers team test upcoming changes that are not yet enabled by default, or if you need to hold back a change that your code depends on but still want to apply other compatibility changes.
Compatibility flags can be set in a Worker’s wrangler.toml
file.
This example enables the specific flag formdata_parser_supports_files
, which is described below. As of the specified date, 2021-09-14
, this particular flag was not yet enabled by default, but, by specifying it in compatibility_flags
, we can enable it anyway. compatibility_flags
can also be used to disable changes that became the default in the past.
Compatibility flags cannot be set or updated on the Cloudflare Dashboard at this time.
Compatibility flags can be set when uploading a Worker using the Workers Script API or Workers Versions API in the request body’s metadata
field.
A growing subset of Node.js APIs are available directly as Runtime APIs, with no need to add polyfills to your own code. To enable these APIs in your Worker, add the nodejs_compat
compatibility flag to your wrangler.toml
:
As additional Node.js APIs are added, they will be made available under the nodejs_compat
compatibility flag. Unlike most other compatibility flags, we do not expect the nodejs_compat
to become active by default at a future date.
The Node.js AsyncLocalStorage
API is a particularly useful feature for Workers. To enable only the AsyncLocalStorage
API, use the nodejs_als
compatibility flag.
Newest changes are listed first.
Default as of | 2024-09-02 |
Flag to enable | allow_custom_ports |
Flag to disable | ignore_custom_ports |
When this flag is enabled, and you specify a port when making a subrequest with the fetch()
API, the port number you specify will be used.
When you make a subrequest to a website that uses Cloudflare (“Orange Clouded”) — only ports supported by Cloudflare’s reverse proxy can be specified. If you attempt to specify an unsupported port, it will be ignored.
When you make a subrequest to a website that doesn’t use Cloudflare (“Grey Clouded”) - any port can be specified.
For example:
With allow_custom_ports the above example would fetch https://example.com:8000
rather than
https://example.com:443
.
Note that creating a WebSocket client with a call to new WebSocket(url)
will also obey this flag.
Default as of | 2024-06-03 |
Flag to enable | blob_standard_mime_type |
Flag to disable | blob_legacy_mime_type |
When calling response.blob.type()
, the MIME type will now be properly extracted from content-type
headers, per the WHATWG spec ↗.
Default as of | 2024-04-29 |
Flag to enable | brotli_content_encoding |
Flag to disable | no_brotli_content_encoding |
When the brotli_content_encoding
compatibility flag is enabled, Workers supports the br
content encoding and can request and respond with data encoded using the Brotli ↗ compression algorithm. This reduces the amount of data that needs to be fetched and can be used to pass through the original compressed data to the client. See the Fetch API documentation for details.
Default as of | 2022-10-31 |
Flag to enable | export_commonjs_default |
Flag to disable | export_commonjs_namespace |
CommonJS modules were previously exporting a module namespace (an object like { default: module.exports }
) rather than exporting only the module.exports
. When this flag is enabled, the export is fixed.
Default as of | 2023-12-01 |
Flag to enable | crypto_preserve_public_exponent |
Flag to disable | no_crypto_preserve_public_exponent |
In the WebCrypto API, the publicExponent
field of the algorithm of RSA keys would previously be an ArrayBuffer
. Using this flag, publicExponent
is a Uint8Array
as mandated by the specification.
Default as of | 2022-06-01 |
Flag to enable | dont_substitute_null_on_type_error |
Flag to disable | substitute_null_on_type_error |
There was a bug in the runtime that meant that when being passed into built-in APIs, invalid values were sometimes mistakenly coalesced with null
. Instead, a TypeError
should have been thrown. The dont_substitute_null_on_type_error
fixes this behavior so that an error is correctly thrown in these circumstances.
Default as of | 2022-10-31 |
Flag to enable | capture_async_api_throws |
Flag to disable | do_not_capture_async_api_throws |
The capture_async_api_throws
compatibility flag will ensure that, in conformity with the standards API, async functions will only ever reject if they throw an error. The inverse do_not_capture_async_api_throws
flag means that async functions which contain an error may throw that error synchronously rather than rejecting.
Default as of | 2022-03-08 |
Flag to enable | no_cots_on_external_fetch |
Flag to disable | cots_on_external_fetch |
The no_cots_on_external_fetch
flag disables the use of the Custom Origin Trust Store when making external (grey-clouded) subrequests from a Cloudflare Worker.
Default as of | 2021-11-10 |
Flag to enable | durable_object_fetch_requires_full_url |
Flag to disable | durable_object_fetch_allows_relative_url |
Originally, when making a request to a Durable Object by calling stub.fetch(url)
, a relative URL was accepted as an input. The URL would be interpreted relative to the dummy URL http://fake-host
, and the resulting absolute URL was delivered to the destination object’s fetch()
handler. This was a mistake — full URLs were meant to be required. This flag makes full URLs required.
Default as of | 2023-03-01 |
Flag to enable | dynamic_dispatch_tunnel_exceptions |
Flag to disable | dynamic_dispatch_treat_exceptions_as_500 |
Previously, when using Workers for Platforms’ dynamic dispatch API to send an HTTP request to a user Worker, if the user Worker threw an exception, the dynamic dispatch Worker would receive an HTTP 500
error with no body. When the dynamic_dispatch_tunnel_exceptions
compatibility flag is enabled, the exception will instead propagate back to the dynamic dispatch Worker. The fetch()
call in the dynamic dispatch Worker will throw the same exception. This matches the similar behavior of service bindings and Durable Objects.
Default as of | 2021-11-10 |
Flag to enable | fetch_refuses_unknown_protocols |
Flag to disable | fetch_treats_unknown_protocols_as_http |
Originally, if the fetch()
function was passed a URL specifying any protocol other than http:
or https:
, it would silently treat it as if it were http:
. For example, fetch()
would appear to accept ftp:
URLs, but it was actually making HTTP requests instead.
Note that Cloudflare Workers supports a non-standard extension to fetch()
to make it support WebSockets. However, when making an HTTP request that is intended to initiate a WebSocket handshake, you should still use http:
or https:
as the protocol, not ws:
nor wss:
.
The ws:
and wss:
URL schemes are intended to be used together with the new WebSocket()
constructor, which exclusively supports WebSocket. The extension to fetch()
is designed to support HTTP and WebSocket in the same request (the response may or may not choose to initiate a WebSocket), and so all requests are considered to be HTTP.
Default as of | 2024-06-03 |
Flag to enable | fetch_standard_url |
Flag to disable | fetch_legacy_url |
The fetch_standard_url
flag makes fetch()
use WHATWG URL Standard ↗ parsing rules. The original implementation would throw TypeError: Fetch API cannot load
errors with some URLs where standard parsing does not, for instance with the inclusion of whitespace before the URL. URL errors will now be thrown immediately upon calling new Request()
with an improper URL. Previously, URL errors were thrown only once fetch()
was called.
Default as of | 2024-03-26 |
Flag to enable | fetcher_no_get_put_delete |
Flag to disable | fetcher_has_get_put_delete |
Durable Object stubs and Service Bindings both implement a fetch()
method which behaves similarly to the global fetch()
method, but requests are instead sent to the destination represented by the object, rather than being routed based on the URL.
Historically, API objects that had such a fetch()
method also had methods get()
, put()
, and delete()
. These methods were thin wrappers around fetch()
which would perform the corresponding HTTP method and automatically handle writing/reading the request/response bodies as needed.
These methods were a very early idea from many years ago, but were never actually documented, and therefore rarely (if ever) used. Enabling the fetcher_no_get_put_delete
, or setting a compatibility date on or after 2024-03-26
disables these methods for your Worker.
This change paves a future path for you to be able to define your own custom methods using these names. Without this change, you would be unable to define your own get
, put
, and delete
methods, since they would conflict with these built-in helper methods.
Default as of | 2021-11-03 |
Flag to enable | formdata_parser_supports_files |
Flag to disable | formdata_parser_converts_files_to_strings |
The FormData
API ↗ is used to parse data (especially HTTP request bodies) in multipart/form-data
format.
Originally, the Workers runtime’s implementation of the FormData
API incorrectly converted uploaded files to strings. Therefore, formData.get("filename")
would return a string containing the file contents instead of a File
object. This change fixes the problem, causing files to be represented using File
as specified in the standard.
Default as of | 2022-03-21 |
Flag to enable | global_navigator |
Flag to disable | no_global_navigator |
With the global_navigator
flag set, a new global navigator
property is available from within Workers. Currently, it exposes only a single navigator.userAgent
property whose value is set to 'Cloudflare-Workers'
. This property can be used to reliably determine whether code is running within the Workers environment.
Flag to enable | html_rewriter_treats_esi_include_as_void_tag |
The HTML5 standard defines a fixed set of elements as void elements, meaning they do not use an end tag: <area>
, <base>
, <br>
, <col>
, <command>
, <embed>
, <hr>
, <img>
, <input>
, <keygen>
, <link>
, <meta>
, <param>
, <source>
, <track>
, and <wbr>
.
HTML5 does not recognize XML self-closing tag syntax. For example, <script src="foo.js" />
does not specify a script element with no body. A </script>
ending tag is still required. The />
syntax simply is not recognized by HTML5 at all and it is treated the same as >
. However, many developers still like to use this syntax, as a holdover from XHTML, a standard which failed to gain traction in the early 2000’s.
<esi:include>
and <esi:comment>
are two tags that are not part of the HTML5 standard, but are instead used as part of Edge Side Includes ↗, a technology for server-side HTML modification. These tags are not expected to contain any body and are commonly written with XML self-closing syntax.
HTMLRewriter
was designed to parse standard HTML5, not ESI. However, it would be useful to be able to implement some parts of ESI using HTMLRewriter
. To that end, this compatibility flag causes HTMLRewriter
to treat <esi:include>
and <esi:comment>
as void tags, so that they can be parsed and handled properly.
Default as of | 2023-03-01 |
Flag to enable | http_headers_getsetcookie |
Flag to disable | no_http_headers_getsetcookie |
Adds the getSetCookie()
↗ method to the Headers ↗ API in Workers.
Default as of | 2024-05-13 |
Flag to enable | internal_stream_byob_return_view |
Flag to disable | internal_stream_byob_return_undefined |
In the original implementation of BYOB (“Bring your own buffer”) ReadableStreams
, the read()
method would return undefined
when the stream was closed and there was no more data to read. This behavior was inconsistent with the standard ReadableStream
behavior, which returns an empty Uint8Array
when the stream is closed.
When the internal_stream_byob_return_view
flag is used, the BYOB read()
will implement standard behavior.
Default as of | 2022-04-05 |
Flag to enable | minimal_subrequests |
Flag to disable | no_minimal_subrequests |
With the minimal_subrequests
flag set, fetch()
subrequests sent to endpoints on the Worker’s own zone (also called same-zone subrequests) have a reduced set of features applied to them. In general, these features should not have been initially applied to same-zone subrequests, and very few user-facing behavior changes are anticipated. Specifically, Workers might observe the following behavior changes with the new flag:
- Response bodies will not be opportunistically gzipped before being transmitted to the Workers runtime. If a Worker reads the response body, it will read it in plaintext, as has always been the case, so disabling this prevents unnecessary decompression. Meanwhile, if the Worker passes the response through to the client, Cloudflare’s HTTP proxy will opportunistically gzip the response body on that side of the Workers runtime instead. The behavior change observable by a Worker script should be that some
Content-Encoding: gzip
headers will no longer appear. - Automatic Platform Optimization may previously have been applied on both the Worker’s initiating request and its subrequests in some circumstances. It will now only apply to the initiating request.
- Link prefetching will now only apply to the Worker’s response, not responses to the Worker’s subrequests.
Default as of | 2022-10-31 |
Flag to enable | url_standard |
Flag to disable | url_original |
The original implementation of the URL
↗ API in Workers was not fully compliant with the WHATWG URL Standard ↗, differing in several ways, including:
-
The original implementation collapsed sequences of multiple slashes into a single slash:
new URL("https://example.com/a//b").toString() === "https://example.com/a/b"
-
The original implementation would throw
"TypeError: Invalid URL string."
if it encountered invalid percent-encoded escape sequences, likehttps://example.com/a%%b
. -
The original implementation would percent-encode or percent-decode certain content differently:
new URL("https://example.com/a%40b?c d%20e?f").toString() === "https://example.com/a@b?c+d+e%3Ff"
-
The original implementation lacked more recently implemented
URL
features, likeURL.canParse()
↗.
Set the compatibility date of your Worker to a date after 2022-10-31
or enable the url_standard
compatibility flag to opt-in the fully spec compliant URL
API implementation.
Refer to the response_redirect_url_standard
compatibility flag , which affects the URL implementation used in Response.redirect()
.
Default as of | 2023-08-01 |
Flag to enable | no_cf_botmanagement_default |
Flag to disable | cf_botmanagement_default |
This flag streamlines Workers requests by reducing unnecessary properties in the request.cf
object.
With the flag enabled - either by default after 2023-08-01 or by setting the no_cf_botmanagement_default
flag - Cloudflare will only include the Bot Management object in a Worker’s request.cf
if the account has access to Bot Management.
With the flag disabled, Cloudflare will include a default Bot Management object, regardless of whether the account is entitled to Bot Management.
Default as of | 2024-03-04 |
Flag to enable | no_global_importscripts |
Flag to disable | global_importscripts |
Suppresses the global importScripts()
function. This method was included in the Workers global scope but was marked explicitly as non-implemented. However, the presence of the function could cause issues with some libraries. This compatibility flag removes the function from the global scope.
Flag to enable | nodejs_als |
Flag to disable | no_nodejs_als |
Enables the availability of the Node.js AsyncLocalStorage ↗ API in Workers. This API allows you to store data that is accessible to all asynchronous operations within a given execution context. This is useful for storing data that is relevant to the current request, such as request-specific metadata or tracing information.
Flag to enable | nodejs_compat |
Flag to disable | no_nodejs_compat |
Enables the full set of available Node.js APIs in the Workers Runtime.
Default as of | 2024-01-29 |
Flag to enable | python_workers |
This flag enables first class support for Python. Python Workers implement the majority of Python’s standard library, support all bindings, environment variable, and secrets, and integration with JavaScript objects and functions via a foreign function interface.
Default as of | 2024-03-18 |
Flag to enable | queues_json_messages |
Flag to disable | no_queues_json_messages |
With the queues_json_messages
flag set, Queue bindings will serialize values passed to send()
or sendBatch()
into JSON format by default (when no specific contentType
is provided).
Default as of | 2022-08-04 |
Flag to enable | r2_list_honor_include |
With the r2_list_honor_include
flag set, the include
argument to R2 list
options is honored. With an older compatibility date and without this flag, the include
argument behaves implicitly as include: ["httpMetadata", "customMetadata"]
.
Default as of | 2024-04-03 |
Flag to enable | rpc |
Flag to disable | no_rpc |
With this flag on, Durable Object stubs and Service Bindings support RPC. This means that these objects now appear as if they define every possible method name. Calling any method name sends an RPC to the remote Durable Object or Worker service.
For most applications, this change will have no impact unless you use it. However, it’s possible some existing code will be impacted if it explicitly checks for the existence of method names that were previously not defined on these types. For example, we’ve seen code in the wild which iterates over bindings and tries to auto-detect their types based on what methods they implement. Such code will now see service bindings as implementing every method, so may misinterpret service bindings as being some other type. In the cases we’ve seen, the impact was benign (nothing actually broke), but out of caution we are guarding this change behind a flag.
Default as of | 2022-01-31 |
Flag to enable | workers_api_getters_setters_on_prototype |
Flag to disable | workers_api_getters_setters_on_instance |
Originally, properties on Workers API objects were defined as instance properties as opposed to prototype properties. This broke subclassing at the JavaScript layer, preventing a subclass from correctly overriding the superclass getters/setters. This flag controls the breaking change made to set those getters/setters on the prototype template instead.
This changes applies to:
AbortSignal
AbortController
Blob
Body
DigestStream
Event
File
Request
ReadableStream
ReadableStreamDefaultReader
ReadableStreamBYOBReader
Response
TextDecoder
TextEncoder
TransformStream
URL
WebSocket
WritableStream
WritableStreamDefaultWriter
Default as of | 2023-03-14 |
Flag to enable | response_redirect_url_standard |
Flag to disable | response_redirect_url_original |
Change the URL implementation used in Response.redirect()
to be spec-compliant (WHATWG URL Standard).
Default as of | 2021-11-10 |
Flag to enable | streams_byob_reader_detaches_buffer |
Flag to disable | streams_byob_reader_does_not_detach_buffer |
Originally, the Workers runtime did not detach the ArrayBuffer
s from user-provided TypedArrays when using the BYOB reader’s read()
method, as required by the Streams spec, meaning it was possible to inadvertently reuse the same buffer for multiple read()
calls. This change makes Workers conform to the spec.
User code should never try to reuse an ArrayBuffer
that has been passed into a BYOB reader’s read()
method. Instead, user code can reuse the ArrayBuffer
backing the result of the read()
promise, as in the example below.
The more recently added extension method readAtLeast()
will always detach the ArrayBuffer
and is unaffected by this feature flag setting.
Default as of | 2022-11-30 |
Flag to enable | streams_enable_constructors |
Flag to disable | streams_disable_constructors |
Adds the work-in-progress new ReadableStream()
and new WritableStream()
constructors backed by JavaScript underlying sources and sinks.
Default as of | 2023-08-01 |
Flag to enable | strict_compression_checks |
Flag to disable | no_strict_compression_checks |
Perform additional error checking in the Compression Streams API and throw an error if a DecompressionStream
has trailing data or gets closed before the full compressed data has been provided.
Default as of | 2023-08-01 |
Flag to enable | strict_crypto_checks |
Flag to disable | no_strict_crypto_checks |
Perform additional error checking in the Web Crypto API to conform with the specification and reject possibly unsafe key parameters:
- For RSA key generation, key sizes are required to be multiples of 128 bits as boringssl may otherwise truncate the key.
- The size of imported RSA keys must be at least 256 bits and at most 16384 bits, as with newly generated keys.
- The public exponent for imported RSA keys is restricted to the commonly used values
[3, 17, 37, 65537]
. - In conformance with the specification, an error will be thrown when trying to import a public ECDH key with non-empty usages.
Default as of | 2022-11-30 |
Flag to enable | transformstream_enable_standard_constructor |
Flag to disable | transformstream_disable_standard_constructor |
Previously, the new TransformStream()
constructor was not compliant with the Streams API standard. Use the transformstream_enable_standard_constructor
to opt-in to the backwards-incompatible change to make the constructor compliant. Must be used in combination with the streams_enable_constructors
flag.
Default as of | 2024-04-01 |
Flag to enable | unwrap_custom_thenables |
Flag to disable | no_unwrap_custom_thenables |
With the unwrap_custom_thenables
flag set, various Workers APIs that accept promises will also
correctly handle custom thenables (objects with a then
method) that are not native promises, but
are intended to be treated as such). For example, the waitUntil
method of the ExecutionContext
object will correctly handle custom thenables, allowing them to be used in place of native promises.
Default as of | 2023-07-01 |
Flag to enable | urlsearchparams_delete_has_value_arg |
Flag to disable | no_urlsearchparams_delete_has_value_arg |
The WHATWG introduced additional optional arguments to the URLSearchParams
object delete()
↗ and has()
↗ methods that allow for more precise control over the removal of query parameters. Because
the arguments are optional and change the behavior of the methods when present there is a risk of
breaking existing code. If your compatibility date is set to July 1, 2023 or after, this compatibility flag will be enabled by default.
For an example of how this change could break existing code, consider code that uses the Array
forEach()
method to iterate through a number of parameters to delete:
The forEach()
automatically passes multiple parameters to the function that is passed in. Prior to the addition of the new standard parameters, these extra arguments would have been ignored.
Now, however, the additional arguments have meaning and change the behavior of the function. With this flag, the example above would need to be changed to:
Default as of | 2023-11-08 |
Flag to enable | vectorize_query_metadata_optional |
Flag to disable | vectorize_query_original |
A set value on vectorize_query_metadata_optional
indicates that the Vectorize query operation should accept newer arguments with returnValues
and returnMetadata
specified discretely over the older argument returnVectors
. This also changes the return format. If the vector values have been indicated for return, the return value is now a flattened vector object with score
attached where it previously contained a nested vector object.
Default as of | 2023-08-15 |
Flag to enable | web_socket_compression |
Flag to disable | no_web_socket_compression |
The Workers runtime did not support WebSocket compression when the initial WebSocket implementation was released. Historically, the runtime has stripped or ignored the Sec-WebSocket-Extensions
header — but is now capable of fully complying with the WebSocket Compression RFC. Since many clients are likely sending Sec-WebSocket-Extensions: permessage-deflate
to their Workers today (new WebSocket(url)
automatically sets this in browsers), we have decided to maintain prior behavior if this flag is absent.
If the flag is present, the Workers runtime is capable of using WebSocket Compression on both inbound and outbound WebSocket connections.
Like browsers, calling new WebSocket(url)
in a Worker will automatically set the Sec-WebSocket-Extensions: permessage-deflate
header. If you are using the non-standard fetch()
API to obtain a WebSocket, you can include the Sec-WebSocket-Extensions
header with value permessage-deflate
and include any of the compression parameters defined in RFC-7692 ↗.
These changes can be enabled via compatibility_flags
, but are not yet scheduled to become default on any particular date.
Flag to enable | html_rewriter_treats_esi_include_as_void_tag |
The HTML5 standard defines a fixed set of elements as void elements, meaning they do not use an end tag: <area>
, <base>
, <br>
, <col>
, <command>
, <embed>
, <hr>
, <img>
, <input>
, <keygen>
, <link>
, <meta>
, <param>
, <source>
, <track>
, and <wbr>
.
HTML5 does not recognize XML self-closing tag syntax. For example, <script src="foo.js" />
does not specify a script element with no body. A </script>
ending tag is still required. The />
syntax simply is not recognized by HTML5 at all and it is treated the same as >
. However, many developers still like to use this syntax, as a holdover from XHTML, a standard which failed to gain traction in the early 2000’s.
<esi:include>
and <esi:comment>
are two tags that are not part of the HTML5 standard, but are instead used as part of Edge Side Includes ↗, a technology for server-side HTML modification. These tags are not expected to contain any body and are commonly written with XML self-closing syntax.
HTMLRewriter
was designed to parse standard HTML5, not ESI. However, it would be useful to be able to implement some parts of ESI using HTMLRewriter
. To that end, this compatibility flag causes HTMLRewriter
to treat <esi:include>
and <esi:comment>
as void tags, so that they can be parsed and handled properly.