Streaming Text Oriented Messaging Protocol
The Streaming Text Oriented Messaging Protocol (STOMP), also known as the Simple Text Oriented Messaging Protocol, is a lightweight, text-based communication protocol designed for asynchronous message passing between clients and message brokers in distributed systems.[1] It provides an interoperable wire format that enables STOMP-compliant clients to interact with any supporting message broker, facilitating cross-language and cross-platform messaging without proprietary dependencies.[2] Originally developed to bridge enterprise message brokers with scripting languages such as Ruby, Python, and Perl, STOMP has evolved into a mature standard for message-oriented middleware (MOM), serving as a simpler alternative to more complex protocols like AMQP.[1]
STOMP operates over reliable, bidirectional streaming transports such as TCP or WebSocket, using a frame-based structure inspired by HTTP requests.[1] Each frame consists of a command (e.g., CONNECT, SEND, or SUBSCRIBE), followed by optional key-value headers, an optional body, and termination by a NULL octet, making it human-readable and easy to implement—even with basic tools like Telnet for client testing.[1] Key features include support for message acknowledgments (auto, client, or client-individual modes), transactions, heart-beating to detect connection issues, and content-type negotiation, all while maintaining backward compatibility across versions.[1] The protocol defines opaque destination strings for routing, allowing server-specific semantics without mandating a particular addressing scheme.[1]
The protocol's versioning began with STOMP 1.0, followed by 1.1 (introducing features like content-length headers and improved error handling), and the current 1.2 specification released in 2012, which refined aspects like frame line endings and message acknowledgments for enhanced reliability.[1] STOMP 1.2 remains the de facto standard, with widespread implementations in frameworks like Spring, Apache ActiveMQ Artemis, and Eclipse Vert.x, supporting real-time applications such as chat systems, financial trading, and IoT messaging.[3][4] Its simplicity and interoperability have made it a popular choice for scalable, event-driven architectures, with ongoing community maintenance via the STOMP specification mailing list.[2]
Introduction
Definition and Purpose
The Streaming Text Oriented Messaging Protocol (STOMP), also known as the Simple Text Oriented Messaging Protocol, is a lightweight, interoperable protocol that defines a text-based wire format for asynchronous message passing between clients and mediating servers, such as message brokers.[1] This format enables STOMP clients to communicate effectively with enterprise messaging systems in a standardized way, supporting core operations without relying on complex binary encodings.[1]
The primary purpose of STOMP is to facilitate asynchronous messaging patterns, including publish-subscribe and point-to-point communication, allowing clients written in various languages to interact with message-oriented middleware (MOM) systems.[1] It was originally designed to bridge the gap for scripting languages like Ruby, Python, and Perl, which often lacked straightforward interfaces to robust enterprise message brokers, thereby promoting easier integration across diverse MOM implementations.[3]
Key design principles of STOMP emphasize simplicity through its human-readable, UTF-8 encoded text format, which is easy to implement and debug across different programming environments.[1] The protocol is transport-agnostic, operating over any reliable bidirectional streaming channel such as TCP or WebSockets, ensuring flexibility without mandating specific lower-layer dependencies.[1] Interoperability is a core philosophy, enabling STOMP to serve as a common denominator for messaging without the overhead of proprietary APIs or binary protocols.[1]
As an application-layer protocol (Layer 7) in the OSI model, STOMP functions within message-oriented middleware to handle high-level messaging semantics, distinct from underlying transport mechanisms.[1]
Historical Development
The Streaming Text Oriented Messaging Protocol (STOMP) emerged in the mid-2000s, developed initially at Codehaus around 2005, to address the challenges faced by scripting languages, such as Ruby, Python, and Perl, in connecting to enterprise message brokers.[3][5] Initially inspired by and formerly known as the Text Oriented Messaging Protocol (TTMP), it was developed as a lightweight, text-based alternative for asynchronous messaging in message-oriented middleware environments.[6]
An informal specification for STOMP version 1.0 appeared in the mid-2000s, establishing core frame structures for basic operations like sending and subscribing to messages over TCP sockets.[7] Version 1.1 followed as a more formalized update in 2011, introducing key features such as optional heart-beating to detect connection failures and content-length headers to accurately delineate message bodies, particularly when null octets were present.[8] These additions improved reliability and compatibility across diverse implementations.
On October 22, 2012, version 1.2 was released, refining server-initiated frames including ERROR for conveying protocol violations and enhanced RECEIPT handling for transaction confirmations, while clarifying heart-beating mechanics and line-ending requirements.[2]
STOMP has no single corporate owner or formal standards body and is maintained collaboratively by the open-source community.[5] The protocol specifications are hosted on GitHub at stomp.github.io, with evolution driven by discussions on the [email protected] mailing list and contributions from developers supporting various brokers and clients.[1]
The protocol's adoption accelerated with the standardization of WebSockets in 2011 (RFC 6455), enabling STOMP-over-WebSocket mappings that facilitated real-time bidirectional messaging in browser-based web applications without proprietary extensions. As of 2025, STOMP continues to find use in IoT deployments and real-time systems for its interoperability and low overhead, even alongside newer protocols like MQTT and AMQP.[9]
Technical Specification
Frame Structure
The fundamental building block of the Streaming Text Oriented Messaging Protocol (STOMP) is the frame, which encapsulates commands, headers, and optional bodies in a structured text format.[1] A STOMP frame begins with a command line, consisting of the command name followed by an end-of-line (EOL) marker, which is optionally a carriage return (CR, octet 13) followed by a line feed (LF, octet 10), or simply LF.[1] This is followed by zero or more header lines, each in the format key:value terminated by EOL, a blank line (EOL without content), an optional body, and finally a null octet (0x00) terminator.[1] The overall structure can be represented as:
COMMAND
header1:value1
header2:value2
body content (optional)
\x00
COMMAND
header1:value1
header2:value2
body content (optional)
\x00
Headers in STOMP frames are UTF-8 encoded strings with case-sensitive keys and values, where keys and values are separated by a colon without spaces around it.[1] Special characters in header values require escaping using C-style backslash sequences: \n for LF, \r for CR, \c for colon, and \\ for backslash itself; any other backslash is treated as literal and may result in a fatal error if undefined.[1] Common headers include destination for routing, content-type to specify the MIME type (defaulting to text/plain if omitted), and content-length indicating the body's octet count in decimal.[1] If multiple headers share the same key, only the first value is considered valid.[1]
The body of a frame, when present, follows the blank line and precedes the null terminator, allowing for text or binary content up to server-imposed limits.[1] Bodies are permitted only in SEND, MESSAGE, and ERROR frames, and their content is UTF-8 encoded by default unless specified otherwise via the content-type header, which supports binary payloads without additional framing restrictions beyond the null terminator.[1] No escaping is applied to the body itself, but it must not contain the null octet, as this would prematurely terminate the frame; servers may reject oversized bodies by sending an ERROR frame and closing the connection.[1]
Framing rules emphasize STOMP's text-oriented nature while accommodating structured data transmission over TCP or other octet streams.[1] All commands and headers must use ASCII-compatible characters in UTF-8, ensuring interoperability, with the null octet serving as the unambiguous frame delimiter even after optional trailing EOLs.[1] This design avoids binary framing complexities, relying instead on text parsing for commands and headers, though binary bodies are supported to enable versatile messaging.[1]
Commands and Responses
The Streaming Text Oriented Messaging Protocol (STOMP) defines a set of client commands and server responses as text-based frames to facilitate messaging operations between clients and brokers. These commands and responses build upon the protocol's frame structure, where each frame includes a command line, optional headers, an optional body, and a null octet terminator.[1]
Client commands initiate actions such as connecting to a server, sending messages, managing subscriptions, handling transactions, and acknowledging receipts. The CONNECT command establishes a connection, requiring the accept-version header to specify supported protocol versions (e.g., 1.2) and the host header for the target server; optional headers include login and passcode for authentication, as well as heart-beat for heartbeat negotiation.[1] The SEND command publishes a message to a destination, mandating the destination header and allowing an optional body for the message content, with the transaction header to group it within a transaction if needed.[1] The SUBSCRIBE command registers interest in messages from a destination, requiring destination and id headers for unique identification, and an optional ack header to set acknowledgment mode (auto, client, or client-individual, defaulting to auto).[1] Conversely, the UNSUBSCRIBE command removes a subscription using the required id header.[1] The DISCONNECT command requests a graceful closure, optionally including a receipt header for confirmation.[1]
Transaction-related commands manage atomic operations across multiple frames. The BEGIN command starts a transaction with a required transaction header providing a unique identifier.[1] The COMMIT command finalizes the transaction using the same transaction identifier, applying all grouped operations.[1] The ABORT command rolls back the transaction, discarding any pending operations tied to the identifier.[1] Acknowledgment commands handle message delivery confirmation in explicit modes. The ACK command confirms receipt of a message via its required id header, optionally within a transaction.[1] The NACK command rejects or requeues a message using the same id and optional transaction parameters.[1]
Server responses acknowledge client actions or deliver content. The CONNECTED frame confirms a successful connection, requiring the version header to indicate the negotiated protocol version and optionally including session for a unique session ID, server for implementation details, and heart-beat for timing.[1] The MESSAGE frame delivers a message to a subscriber, mandating destination, message-id, and subscription headers, with an optional ack header in client acknowledgment modes and a body for content.[1] The RECEIPT frame provides confirmation for operations requesting it, requiring the receipt-id header matching the client's request.[1] The ERROR frame signals failures, optionally with a message header for a brief description and a detailed body; it closes the connection upon receipt.[1]
Key headers unique to these operations include those for acknowledgments, receipts, and transactions. The ack header specifies modes in SUBSCRIBE (auto for automatic acknowledgment, client for batch, client-individual for per-message) and appears in MESSAGE to indicate required explicit acknowledgments.[1] The receipt header, used in most client commands except CONNECT, requests a RECEIPT response with a unique receipt-id.[1] The transaction header groups compatible commands like SEND, ACK, and NACK under a BEGIN block for atomicity.[1] These elements ensure reliable, interoperable messaging in STOMP version 1.2.[1]
Connection and Messaging Operations
Establishing Connections and Sessions
The process of establishing a connection in the Streaming Text Oriented Messaging Protocol (STOMP) begins with the client sending a CONNECT frame to the server over an underlying transport such as TCP or WebSocket.[1] This frame includes mandatory headers like accept-version, which specifies the highest STOMP version the client supports (e.g., "1.2"), and host, identifying the target server.[1] Optional headers may include login and passcode for authentication, as well as heart-beat to negotiate keepalive intervals.[1] Upon receipt, the server evaluates the request: if the versions are compatible, it responds with a CONNECTED frame containing the selected version, a unique server-assigned session identifier, and optional details like the server implementation name.[1] Incompatible versions prompt an ERROR frame from the server, followed by connection closure.[1]
Once connected, a STOMP session is established and persists until the client explicitly sends a DISCONNECT frame or the transport connection terminates.[1] The session identifier, provided in the CONNECTED frame's session header, uniquely identifies the session on the server and enables stateful interactions, such as transaction management.[1] Servers assign sessions dynamically, ensuring persistence across operations until disconnection, which helps maintain context for ongoing messaging activities.[1]
Heart-beating, introduced in STOMP version 1.1 and refined in 1.2, provides a mechanism to detect connection failures and prevent idle timeouts imposed by underlying transports.[1] During connection negotiation, both client and server specify heart-beat intervals via the heart-beat header in the format "tx,rx" (e.g., "0,30000" for server-to-client every 30 seconds), where tx indicates the client's transmission interval to the server and rx the server's to the client, both in milliseconds.[1] If enabled, parties send empty frames (a single newline) at the negotiated rate when no other data is transmitted; failure to receive heart-beats within a timeout period signals a dead connection, prompting closure.[1] This feature is configurable and optional, with the CONNECTED frame echoing the effective intervals.[1]
Security in STOMP connection establishment relies primarily on optional authentication via the login and passcode headers in the CONNECT frame, which the server verifies against its credentials store.[1] Invalid credentials result in an ERROR frame and connection rejection.[1] The protocol itself does not provide built-in encryption or advanced authorization; instead, it depends on the underlying transport for security, such as TLS for confidentiality and integrity.[1] Version 1.2 introduces improved error handling, including more descriptive ERROR frames during connection setup, to enhance robustness without altering core security primitives.[1]
Publishing, Subscribing, and Acknowledgements
In STOMP, publishing messages occurs through the SEND operation, where a client transmits a message to a specified destination, such as a queue or topic, using the required destination header to route the content. The message body carries the payload, while optional headers like content-type and content-length provide metadata for proper handling, and the protocol supports broker-specific extensions for attributes such as expiration and priority to influence message lifecycle and ordering.[1]
Subscribing to receive messages involves the SUBSCRIBE operation, in which a client registers interest in messages from a particular destination by specifying both the destination and a unique id for the subscription. The ack header configures the acknowledgment mode: auto (the default), where the server assumes delivery upon sending without client confirmation; client, requiring cumulative acknowledgments from the client for all messages up to a point; or client-individual, mandating per-message acknowledgments. Upon subscription, the server delivers matching messages to the client via MESSAGE frames.[1]
Acknowledgements ensure reliable message delivery in manual modes, with clients issuing an ACK frame to confirm processing of a specific message identified by its id header from the received MESSAGE frame. In client mode, an ACK cumulatively confirms all unacknowledged messages preceding the specified one, while in client-individual mode, it applies only to the individual message; conversely, a NACK frame signals rejection, allowing server-specific behaviors like redelivery. The auto mode delegates all acknowledgments to the server, eliminating client involvement.[1]
Error handling during publishing or subscribing prompts the server to issue an ERROR frame, which includes a descriptive message header and optional body detailing the issue, such as an invalid destination or malformed request, after which the connection is closed to prevent further operations.[1]
Transactions enable atomic grouping of multiple SEND and ACK/NACK operations across a session by initiating with a BEGIN frame using a unique transaction identifier, followed by the grouped actions, and concluding with either COMMIT to apply all changes or ABORT to roll them back; any uncommitted transaction automatically aborts upon disconnection.[1]
Examples
Basic Send and Receive
The basic send and receive operation in STOMP demonstrates the protocol's core functionality for point-to-point messaging between clients via a broker, using only essential frames over a plain TCP connection.[1] In this minimal scenario, one client establishes a connection, publishes a single message to a queue destination, while another client connects, subscribes to the same destination in auto-ack mode (where messages are automatically acknowledged upon receipt), and consumes the message.[1] No authentication, transactions, or advanced headers are involved, assuming a broker that supports STOMP version 1.2 and defaults to auto-acknowledgment for subscriptions.[1]
STOMP communication relies on text-based frames, each consisting of a command line, optional headers, a body (if present), and a null octet (0x00) terminator.[1] The process begins with the publishing client sending a CONNECT frame to negotiate the protocol version and host, followed by the broker's CONNECTED response confirming the session.[1] The publishing client then issues a SEND frame specifying the destination queue and message body.
Here is the verbatim frame sequence for the publishing client:
CONNECT
accept-version:1.2
host:example-broker
\x00
CONNECTED
version:1.2
\x00
SEND
destination:/queue/foo
Hello
\x00
CONNECT
accept-version:1.2
host:example-broker
\x00
CONNECTED
version:1.2
\x00
SEND
destination:/queue/foo
Hello
\x00
The \x00 represents the null terminator required at the end of each frame.[1]
Concurrently, the consuming client follows a similar connection sequence but adds a SUBSCRIBE frame to register interest in the /queue/foo destination, using auto-ack mode (the default if no ack header is specified).[1] Upon receiving the subscription, the broker delivers the pending message via a MESSAGE frame, which includes the destination and body for the consumer to process.[1]
The corresponding frames for the consuming client are:
CONNECT
accept-version:1.2
host:example-broker
\x00
CONNECTED
version:1.2
\x00
SUBSCRIBE
id:sub-001
destination:/queue/foo
\x00
MESSAGE
subscription:sub-001
destination:/queue/foo
message-id:msg-001
Hello
\x00
CONNECT
accept-version:1.2
host:example-broker
\x00
CONNECTED
version:1.2
\x00
SUBSCRIBE
id:sub-001
destination:/queue/foo
\x00
MESSAGE
subscription:sub-001
destination:/queue/foo
message-id:msg-001
Hello
\x00
This exchange ensures reliable delivery of the "Hello" message from publisher to consumer without explicit acknowledgments, highlighting STOMP's simplicity for basic queuing.[1]
Subscription and Transaction Example
In the Streaming Text Oriented Messaging Protocol (STOMP), a client can subscribe to a destination with manual acknowledgements to ensure reliable message delivery, while transactions allow grouping multiple operations atomically. Consider a scenario where a client subscribes to a queue for processing messages, manually acknowledges each upon successful handling, and then uses a transaction to batch-send multiple related messages before committing them to the broker. This approach guarantees that acknowledgements and sends either all succeed or all fail, preventing partial updates in distributed systems.[1]
The subscription begins with a SUBSCRIBE frame specifying the ack:client mode, which requires the client to explicitly acknowledge each received message; unacknowledged messages may be redelivered by the broker. The frame includes required headers such as id for the subscription identifier and destination for the target queue or topic. For example:
SUBSCRIBE
id:sub-123
destination:/queue/orders
ack:client
receipt: sub-receipt
SUBSCRIBE
id:sub-123
destination:/queue/orders
ack:client
receipt: sub-receipt
Upon subscription, the broker sends MESSAGE frames to the client, each including a subscription header matching the subscription id, a unique message-id or ack header for acknowledgement, and the destination. The client processes the message and sends an ACK frame referencing the ack value to confirm delivery. To incorporate transactions, the client first issues a BEGIN frame with a transaction header, then includes the same transaction header in subsequent ACK and SEND frames, before finalizing with a COMMIT frame. This ensures all operations within the transaction are processed atomically. An optional receipt header in SEND frames can solicit confirmation from the broker. A sample sequence for receiving one message and batch-sending two in a transaction follows:
SUBSCRIBE
id:sub-123
destination:/queue/orders
ack:client
receipt: sub-receipt
[Broker response: RECEIPT\nreceipt-id:sub-receipt\n\n\0]
MESSAGE
subscription:sub-123
message-id:msg-456
destination:/queue/orders
ack:ack-456
content-length:15
Order processed
\0
BEGIN
transaction:tx-789
receipt: begin-receipt
ACK
id:ack-456
transaction:tx-789
SEND
destination:/queue/confirmations
transaction:tx-789
receipt: send1-receipt
Confirmation 1
\0
SEND
destination:/queue/confirmations
transaction:tx-789
receipt: send2-receipt
Confirmation 2
\0
COMMIT
transaction:tx-789
receipt: commit-receipt
SUBSCRIBE
id:sub-123
destination:/queue/orders
ack:client
receipt: sub-receipt
[Broker response: RECEIPT\nreceipt-id:sub-receipt\n\n\0]
MESSAGE
subscription:sub-123
message-id:msg-456
destination:/queue/orders
ack:ack-456
content-length:15
Order processed
\0
BEGIN
transaction:tx-789
receipt: begin-receipt
ACK
id:ack-456
transaction:tx-789
SEND
destination:/queue/confirmations
transaction:tx-789
receipt: send1-receipt
Confirmation 1
\0
SEND
destination:/queue/confirmations
transaction:tx-789
receipt: send2-receipt
Confirmation 2
\0
COMMIT
transaction:tx-789
receipt: commit-receipt
[Broker responses: RECEIPT frames for each receipt header, confirming subscription, begins, sends, and commit.][1]
In client-ack mode, if a message cannot be processed, the client sends a NACK frame with the message's id instead of ACK, potentially triggering redelivery to the same or another client, depending on broker policy. For instance:
NACK
id:ack-456
transaction:tx-789
NACK
id:ack-456
transaction:tx-789
This rejects the message within the transaction, allowing rollback via ABORT if needed, ensuring no partial commitments occur.[1]
Implementations
Message Broker Support
Apache ActiveMQ provides comprehensive support for the STOMP protocol in both its Classic and Artemis distributions. ActiveMQ Classic enables STOMP over TCP and includes a JMS mapping that allows STOMP clients to interact with JMS destinations, facilitating interoperability with Java applications.[10] ActiveMQ Artemis, the modern successor, fully implements STOMP versions 1.0, 1.1, and 1.2 over TCP and WebSockets, with features like heart-beating, large message handling, and JMS bridging for enhanced reliability and cross-protocol communication.[4]
RabbitMQ integrates STOMP via a core plugin that supports versions 1.0 through 1.2, including extensions for durable subscriptions and error receipts. This plugin maps STOMP destinations to underlying AMQP 0.9.1 exchanges and queues, enabling STOMP clients to leverage RabbitMQ's robust routing and clustering capabilities. Additionally, the Web STOMP plugin extends support over WebSockets, allowing direct connectivity from web browsers without polling.[11][12]
Apache Apollo, a legacy message broker based on the original ActiveMQ foundations, offers native STOMP support for versions 1.0 to 1.2, with built-in WebSocket transport for simplified browser integration. Designed for high performance and ease of maintenance, Apollo emphasizes STOMP as a core protocol but has been superseded by ActiveMQ Artemis since its last major release in 2015.[13]
As the successor to HornetQ, Apache ActiveMQ Artemis inherits and expands STOMP compatibility through configurable acceptors that handle versions 1.0 to 1.2. HornetQ originally provided STOMP interoperability for cross-language clients, including support for JMS-like semantics, which Artemis enhances with non-blocking architecture and multi-protocol bridging.[4][14]
IBM MQ does not natively support STOMP and typically requires third-party adapters or bridges for integration in specific scenarios.
As of 2025, STOMP maintains relevance in message brokers for IoT applications due to its lightweight, text-oriented design that simplifies integration with resource-constrained devices, though many brokers prioritize AMQP and MQTT for superior efficiency in high-volume, bandwidth-limited environments.[15]
Several client libraries implement the STOMP protocol across various programming languages, facilitating connections to message brokers over TCP or WebSocket transports. These libraries emphasize simplicity and interoperability, allowing developers to publish and subscribe to messages with minimal boilerplate code. Many support STOMP versions 1.0 through 1.2, including features like heart-beating for connection health monitoring and frame acknowledgments.[16]
In Java, the Spring Framework provides a robust STOMP client via its WebSocket and TCP modules, enabling seamless integration in enterprise applications for real-time messaging over WebSockets. This client supports automatic reconnection and SockJS fallback for browser compatibility. In October 2025, Spring Framework versions 6.2.12, 6.1.24, and 5.3.46 addressed a STOMP over WebSocket CSRF vulnerability (CVE-2025-41254).[17] Additionally, Apache ActiveMQ offers STOMP support through its JMS mapping, allowing Java developers to use familiar JMS APIs while communicating via STOMP frames.[10]
For JavaScript environments, including browsers and Node.js, the stompjs library delivers a full-featured STOMP client optimized for WebSocket connections, supporting versions 1.0 to 1.2 with built-in heart-beating and broker-specific extensions like RabbitMQ compatibility. It simplifies client-side development by handling protocol framing and error recovery automatically.[18]
Python developers can use stomp.py, a comprehensive library that implements the full STOMP protocol over both TCP and WebSocket, including support for transactions and receipts. It includes a built-in command-line interface for quick testing.[19]
Other languages offer dedicated STOMP clients as well. The Ruby stomp gem provides an easy-to-use interface for connecting to brokers, supporting STOMP 1.0 to 1.2 with asynchronous message handling. In Perl, Net::STOMP::Client offers object-oriented access to STOMP operations, compatible with versions up to 1.2. For Go, the stompngo package implements a lightweight client with full protocol adherence, including 1.2 features like enhanced error frames. In C#, Apache NMS.ActiveMQ serves as a JMS-like client for STOMP, enabling .NET applications to interact with brokers via TCP or SSL-secured connections.[20][21][22][23]
Command-line tools enhance testing and debugging of STOMP interactions. Stompcat, bundled with the Ruby stomp gem, acts as a versatile CLI client akin to netcat, allowing users to send and receive STOMP messages interactively over TCP or WebSocket.[20]