OP_RETURN’s Journey
What is OP_RETURN and Why Does It Exist?
In Bitcoin, OP_RETURN is a special script opcode that allows embedding arbitrary data into a transaction output while rendering that output provably unspendable. An output with an OP_RETURN
script (commonly called a null data output) carries data after the OP_RETURN
opcode, but because OP_RETURN immediately marks the script as invalid, no one can ever spend those coins. Such outputs are thus prunable (they need not be stored in the UTXO set), making them a safer way to insert data into the blockchain without permanently bloating every node’s UTXO database. By design, standard policy requires an OP_RETURN output to have zero satoshis (no bitcoin value), to ensure no spendable coins get accidentally burned in these unspendable outputs.
Why was OP_RETURN introduced? Before 2014, some users found ad-hoc ways to encode data on-chain for example, by putting data into fake multi-signature outputs or other script fields which did pollute the UTXO set or otherwise abuse script opcodes. This raised concerns about blockchain bloat. In response, Bitcoin Core 0.9.0 (released March 2014) added OP_RETURN as a standard output type, providing a “safe” outlet for small amounts of data: any data placed after an OP_RETURN would go in an provably unspendable output. The idea was to discourage on-chain data storage while still accommodating limited use cases like cryptographic commitments or timestamps. Early Bitcoin Core developers stressed that this was not an endorsement of arbitrary data on-chain, but a harm-reduction measure e.g. the 0.9.0 release notes emphasized that storing data in the blockchain is generally a bad idea and that OP_RETURN was meant to reduce negative impacts of such practices.
Historical Limits on OP_RETURN Data
From the start, Bitcoin Core enforced a size limit on OP_RETURN data as a standard policy rule (not a consensus rule more on that later). The limit has changed over time:
Bitcoin Core 0.9.0 (2014): OP_RETURN outputs were relayed/mined only if the embedded data payload was 40 bytes or smaller, and only one OP_RETURN output was allowed per transaction. This 40-byte cap was chosen after debate initially 80 bytes was proposed, but some developers (notably Luke Dashjr) argued even 80 bytes was too much, so they settled on 40 bytes as a conservative limit. Any transaction with a larger OP_RETURN would still be valid if mined, but default nodes would treat it as non-standard and refuse to relay it. Bitcoin Core 0.11.0 (2015): The default OP_RETURN policy limit was raised to 80 bytes of data. This effectively doubled the allowed metadata size, reflecting growing comfort with certain use-cases (e.g. 80 bytes can store a 64-byte hash and some extra data) and community feedback that 40 bytes was too restrictive. The single-output rule remained: transactions were still only considered standard if they had at most one OP_RETURN output. Bitcoin Core 0.12.0 (2016): The limit was adjusted to 83 bytes total script size (roughly 80 bytes of data plus up to ~3 bytes for the OP_RETURN opcode and any pushdata length encoding). In other words, the maximum payload remained 80 bytes, but the way it was counted changed since OP_RETURN scripts include 1 byte for the OP_RETURN
itself and 1–2 bytes for the length push opcode, the total allowed scriptPubKey length became 83 bytes. This was essentially a technical tweak rather than a true expansion of data capacity. Notably, Bitcoin Core 0.12.0 also lifted a prior restriction that required the data to be in a single push: it became standard to have multiple pushdata items (or even numeric opcodes) after an OP_RETURN, as long as the entire script stayed within the 83-byte limit. The policy of only a single OP_RETURN output per transaction continued if you include two or more OP_RETURN outputs, the transaction is treated as non-standard by default nodes. (This one-output rule exists to prevent users from easily bypassing the size limit by splitting data across many outputs.)
Throughout these changes, the intention was to allow enough space for legitimate uses (like embedding a hash or short message) but discourage large-scale data storage. The -datacarriersize
configuration option was provided so that miners or node operators could manually set a larger (or smaller) limit if they wished. For instance, some miners chose to increase the limit to accommodate certain projects, meaning they would include transactions with bigger OP_RETURNs even though the default policy would not relay them.
Policy vs. Consensus: Standardness Rules vs. Network Rules
It’s critical to understand that the OP_RETURN size limits and one-output policy are standardness (policy) rules, not consensus rules. This means they affect the default behavior of Bitcoin Core nodes (what transactions they will relay or mine), but they do not determine what transactions are valid in the blockchain.
Consensus rules are those everyone must follow for the blockchain to remain consistent for example, the 4 million weight unit (~4 MB) block weight limit is a consensus rule. There is no consensus rule limiting an OP_RETURN payload to 80 bytes. In fact, under consensus, an output script (including OP_RETURN data) can be as large as the general maximum script size of 10,000 bytes (as long as no single push is over 520 bytes, per script rules). So, a transaction could technically have an OP_RETURN output with several kilobytes of data and still be valid on-chain. Likewise, having multiple OP_RETURN outputs in one transaction is perfectly valid by consensus. The Bitcoin protocol itself doesn’t treat OP_RETURN specially it’s Bitcoin Core’s policy that, by default, refuses to relay or mine transactions violating the size or count limits.
Standardness rules serve as a gentle filter to protect the network from abuse and to guide wallet behavior. If a transaction is “non-standard” (e.g. it contains two OP_RETURN outputs, or a 200-byte OP_RETURN message), Bitcoin Core nodes won’t relay it and most miners won’t include it. However, a miner is free to override these rules. In practice, this has happened: miners and users have found workarounds to embed larger amounts of data when desired. Some tactics have included splitting data across multiple transactions, encoding data in fake public keys or spendable script fields (to evade the OP_RETURN limit entirely), or using witness data in SegWit outputs for embedding content. These workarounds can be ugly for example, encoding data in a normal output means those outputs stick around in the UTXO set or use resources in unintended ways. Still, because consensus rules don’t forbid it, such transactions have been mined (often by miners running with a raised -datacarriersize
or custom patch). In short, the 80-byte limit has always been a soft barrier. It constrained the default network policy, but anyone determined to store more data on-chain could circumvent it by either negotiating directly with miners or by disguising the data.
To illustrate, after the OP_RETURN rule was established, protocols like Counterparty and Omni, which encode token data in Bitcoin transactions, initially had to get creative to stay within 40 or 80 bytes or otherwise split data across outputs. In recent times, with the popularity of NFT-like “inscriptions” (e.g. Ordinals) and other on-chain content, we even saw data being put into Taproot witness data or other scripts because OP_RETURN was too limited. This context set the stage for re-evaluating whether the Core software’s artificial 80-byte limit was doing more harm than good.
Creating Transactions with OP_RETURN: Developer Guide and Best Practices
From a developer’s perspective, using OP_RETURN in a transaction is straightforward, but it does require constructing a custom output. Standard wallet software (like Bitcoin Core’s GUI or typical mobile wallets) usually doesn’t provide a direct “add message” feature, so developers use lower-level tools or libraries. Here’s how it works in practice:
Using Bitcoin Core CLI/RPC: Bitcoin Core’s JSON-RPC interface allows creating transactions with arbitrary outputs through the createrawtransaction
call. When building outputs, instead of specifying a destination address, you can use a special “data” output. For example, one might call:t
createrawtransaction '[]' '{"data":"<hex data>"}'
In this RPC, the first argument is a list of inputs (in this simple example, an empty list []
meaning we’ll later fund it with coins from our wallet or UTXOs), and the second argument is a map of outputs. By putting "data
" as the key, with a hex string as the value, Bitcoin Core will create an output script that contains an OP_RETURN followed by that hex-encoded data, and it will assign 0 BTC to that output’s value. Bitcoin Core automatically sets the output value to 0 (since any value there would be unspendable and would essentially be burned; Core treats burning via OP_RETURN as allowed even for 0). The hex string can represent any binary data you want to embed, up to the byte limit. After creating the raw transaction, you’d typically use fundrawtransaction
to add actual inputs and the necessary fee (because an OP_RETURN output still makes the transaction larger, so you must pay fees for those bytes). Then you’d use signrawtransactionwithwallet
(or with key, depending on your setup) to sign any inputs, and finally sendrawtransaction
to broadcast it. Bitcoin Core will relay it as long as the data size is within the standard limit (and datacarrier
is not disabled).
Using bitcoinjs-lib (JavaScript library): If you’re building transactions programmatically in a development environment, libraries like bitcoinjs-lib make it easy to add an OP_RETURN output. Bitcoinjs-lib provides a script utility specifically for creating a null data script (another term for an OP_RETURN locking script). For instance, you can do something like:
const bitcoin = require('bitcoinjs-lib');
const data = Buffer.from("Hello, Bitcoin!");
const script = bitcoin.script.nullData.output.encode(data);
Here, script
will contain the binary script for OP_RETURN "Hello, Bitcoin!"
. Under the hood, this encodes the data length and content as a push after the OP_RETURN opcode. Once you have this script, you can add it as an output in a transaction. If using the newer Psbt (Partially Signed Bitcoin Transaction) interface or TransactionBuilder, it might look like:
txb.addOutput(script, 0);
This means “add an output with this script and value 0”. After that, you’d add whatever other outputs or inputs you need (e.g., maybe another output sending some BTC to a recipient, and inputs funding the transaction). Then sign the inputs and build the transaction. The end result is a hex transaction that you can broadcast via any node. If the data buffer exceeds 80 bytes, you have to be mindful that by default Bitcoin Core nodes might not relay it you’d need either direct miner interaction or run a node with a custom policy. But for typical uses under 80 bytes, the above works out of the box.
Bitcoinjs-lib also offers a convenience called bitcoin.payments.embed({ data: [Buffer.from('...')] })
which essentially does the same thing: it creates an embed payment whose output script is an OP_RETURN carrying the provided data. This can be more straightforward for some developers. Other libraries in different languages have similar capabilities (for example, Bitcoin’s C++ library or Python libraries allow manual script assembly with OP_RETURN plus data). The key is that the OP_RETURN opcode is represented by the hex byte 0x6a
, and it is followed by a pushdata opcode that indicates how many bytes of data follow. For small data (≤75 bytes), the push opcode is just the byte of the length. For example, 20 bytes of data would be preceded by a single byte 0x14
(which is 20 in hex). For lengths above 75, Bitcoin Script uses an extended push format (OP_PUSHDATA1, etc.), but this is handled automatically by libraries.
Technical Constraints and Safeguards
A key point in this discussion is that removing the dedicated OP_RETURN size limit does not remove all constraints on stuffing data into Bitcoin. Even with no 80-byte cap, every transaction and block must still obey Bitcoin’s overarching size limits and DoS protections:
Transaction Size/Weight Limit: Standard Bitcoin Core policy will not relay transactions larger than 100,000 virtual bytes (which corresponds to 400,000 weight units). This rule remains unchanged. Thus, even if you wanted to create a huge transaction full of OP_RETURN outputs, you’d be capped at around 100 KB (assuming default relay policy) per transaction. In fact, consensus rules themselves don’t allow any single transaction to be bigger than a block, so ~1MB is an absolute upper bound in practice (and 100 kB is the practical relay limit). This is an important DoS safeguard independent of OP_RETURN. Block Weight Limit: Blocks are still limited by consensus to 4,000,000 weight units (4 MWU), which is equivalent to the ~1MB base + 3MB witness data limit of SegWit. No matter how many OP_RETURN outputs one tries to pack in, a block can never exceed this weight. Most of the data in OP_RETURN outputs is “base” blockchain data (since it’s part of outputs), so it counts fully against the block weight. In other words, miners cannot suddenly fill blocks with, say, 10 MB of pictures or text the 4MWU block size ceiling remains a hard safety limit. Other limits: Dust rules and script rules still apply. OP_RETURN outputs must still carry 0 satoshis (and any output with tiny value below the dust threshold won’t be relayed either). Each push opcode is limited to 520 bytes, and the total script length per output cannot exceed 10,000 bytes by consensus. Also, there are limits on how many signature operations a transaction can have and how many in-mempool ancestors/descendants, which collectively act to prevent pathological transactions from causing excessive validation work. In short, all the usual protections against oversized or overly complex transactions remain in force. The removal of the 80-byte OP_RETURN cap simply means that within those broader limits, the software won’t specifically penalize a transaction for having a lot of data in an OP_RETURN.
This context has been emphasized by developers to assuage fears. As one summary put it: even without the datacarrier limit, “blocks remain limited to 4 million weight units; dust outputs are still rejected; [and] ... the 100kB transaction weight limit ... nobody is proposing to change”. The idea is that if someone wants to pay for a larger data-storing transaction, they’ll quickly run into the fee market and block size constraint large transactions have to pay higher fees to outcompete others for inclusion, and miners have an incentive to include whichever transactions pay them more (whether they contain data or regular payments). In essence, market forces and existing limits form the boundary for data usage, instead of an arbitrary 80-byte line in the sand.
Final Thoughts
The activation of unlimited OP_RETURN outputs in Bitcoin Core’s default policy marks a significant evolution in Bitcoin’s functionality. What was once a controversial proposal is now a settled reality: Bitcoin can serve not only as a ledger of financial transactions but also as a flexible data publication platform, within the bounds of its consensus rules. The change has been implemented carefully so as not to compromise network health – miners, nodes, and users are all playing by the same rules, and those rules are designed to let innovation flourish while the fee market and block limits keep things in check.
For developers and businesses, the new OP_RETURN policy is an invitation to build useful, creative, and responsible on-chain data applications. Whether it’s timestamping important records, issuing digital assets, writing messages, or supporting Layer-2/3 protocols with on-chain anchors, you can now do more with a single Bitcoin transaction than ever before. By adhering to the best practices outlined above, you can take advantage of this feature while respecting the resource constraints of the network.
Bitcoin’s core ethos of decentralization and permissionless innovation is upheld in this change – rather than forbidding certain usages via hard-coded limits, the network entrusts the open market and its users to decide how to utilize block space (and pay for it). As of the latest release, OP_RETURN is simply another tool in the Bitcoin toolbox, fully supported and here to stay. We expect the ecosystem to adapt quickly and see new patterns of usage emerge. As always, developers should stay informed via Bitcoin Core release notes and documentation for any further tweaks (for example, eventual removal of the deprecated config options) but the direction is clear: OP_RETURN’s evolution has arrived at its intended destination – a straightforward, unrestricted means of embedding data on the Bitcoin blockchain, governed by the same transparent rules that secure the rest of the network.