MLLP vs HTTP for HL7 messaging
MLLP and HTTP both carry HL7 messages but differ in connection model, routing, load balancing, and observability. When to use which, and why the choice is rarely yours.
MLLP carries HL7 v2 messages over persistent TCP connections. HTTP carries HL7 FHIR resources as RESTful API calls. Both transport healthcare data, but they come from different eras, serve different protocol versions, and behave differently in production.
In most cases, you don’t choose between them. The systems on each end of the interface dictate the transport. A lab instrument from 2010 speaks MLLP. A cloud-based EHR speaks HTTP. The decision was made before you arrived.
When you do have a choice, the differences that matter are operational: connection lifecycle, load balancing, observability, and failure handling.
Connection model
MLLP opens a TCP connection and keeps it open. Messages flow over the same connection sequentially: one message, one ACK, next message, next ACK. The connection carries state: if a message is in flight, the connection is busy.
HTTP is stateless by design. Each request is independent. The client sends a request, gets a response, and the connection can be reused for a completely unrelated request. Even with HTTP keep-alive, requests don’t depend on each other.
The practical consequence: MLLP connections are long-lived. A sending system might open a connection at startup and keep it for days or weeks. HTTP connections are short-lived or pooled. A client might open a connection, send a few requests, and let it close after an idle timeout.
Long-lived connections create operational challenges. A network device that resets idle TCP connections (a common firewall behavior) will break MLLP connections silently. The sender thinks the connection is open. The receiver has already cleaned it up. The next message fails, and the sender has to reconnect. HTTP connections are short enough that this rarely matters.
MLLP connections also interact poorly with rolling deployments. When a receiver pod restarts, every persistent connection to that pod breaks. The sender must detect the broken connection and reconnect. With HTTP, the load balancer routes the next request to a healthy pod automatically.
Message framing
MLLP uses three delimiter bytes: 0x0B to start, 0x1C 0x0D to end. The
receiver scans the byte stream for these delimiters to find message boundaries.
There are no headers, no content type, no metadata outside the HL7 message
itself.
HTTP carries metadata in headers. Content-Type declares the format.
Content-Length declares the size. Custom headers can carry routing hints,
authentication tokens, or trace IDs. The message body is just one part of a
structured request.
For HL7, this means HTTP requests are self-describing. A receiver can inspect headers before parsing the body. An MLLP receiver has to parse the HL7 MSH segment to understand what the message is. The transport layer provides no metadata.
Routing
MLLP routes by IP address and port. Each receiver listens on a port, and the sender connects to that address. If you need different processing for ADT messages and ORU messages, you either run two receivers on different ports or the single receiver parses the message and routes internally.
HTTP routes by URL path, headers, and method. A single receiver can expose
/hl7/adt and /hl7/oru as different endpoints. Load balancers, API gateways,
and reverse proxies can route based on URL patterns without parsing the message
body.
This distinction matters for multi-tenant systems. An HTTP-based HL7 receiver can route messages from different senders to different handlers using URL paths or API keys in headers. An MLLP receiver needs either separate ports per sender or application-level routing after parsing.
Load balancing
HTTP load balancing is well-understood infrastructure. Any load balancer understands HTTP semantics and can route requests based on headers, distribute them round-robin, and health-check backends with HTTP GET requests.
MLLP load balancing is TCP load balancing. The load balancer sees an opaque byte stream. It can’t inspect the HL7 content or make routing decisions based on message type. More importantly, MLLP requires session affinity. All messages on a connection go to the same backend. Without session affinity, a load balancer might distribute bytes from a single MLLP frame across two backends, breaking the protocol.
Kubernetes Services support session affinity with sessionAffinity: ClientIP.
This pins a sender’s connections to one pod for a configurable timeout. It
works, but it means traffic distribution is uneven if some senders are more
active than others.
Acknowledgment
MLLP acknowledgment is synchronous and in-band. The sender writes a message, the receiver processes it and writes an ACK on the same TCP connection. The sender blocks until the ACK arrives. One message in flight per connection.
HTTP acknowledgment is the response status code. A 200 OK means the server
accepted the request. A 400 Bad Request means the payload is invalid. A
503 Service Unavailable means try again later. The semantics are similar to
HL7 ACK codes (AA maps roughly to 200, AR to 400, AE to 503) but the
mechanisms differ.
HTTP also supports asynchronous patterns. The server can return 202 Accepted
(received but not yet processed) and provide a callback URL or polling endpoint
for status. MLLP has no equivalent. The ACK is the only feedback mechanism.
HL7 v2 enhanced acknowledgment mode adds a two-phase pattern (accept acknowledgment followed by application acknowledgment), but it’s rarely used in practice. Most interfaces use original mode: one message, one ACK.
Observability
HTTP gives you observability for free. Request logs include method, path, status
code, and latency. Metrics follow standard patterns: request rate, error rate,
latency percentiles. Distributed tracing propagates through standard headers
like traceparent. Every monitoring tool understands HTTP.
MLLP has no built-in observability. The protocol doesn’t define logging fields, metrics, or trace propagation. Everything is custom. You parse the MSH segment to extract the message type and control ID for logging. You instrument the processing pipeline manually for metrics. Trace context, if you need it, has to be embedded in an HL7 field (ZTR segments are sometimes used for this) because the transport has nowhere else to put it.
This gap is one of the strongest arguments for HTTP when you have a choice. Not because MLLP can’t be instrumented, but because the tooling ecosystem around HTTP already exists. With MLLP, you build it yourself.
TLS
Both protocols support TLS. For HTTP, this is HTTPS: standard, universal, supported by every client and server. Certificate management tools like cert-manager, ACME providers, and cloud-managed certificates all target HTTPS.
For MLLP, TLS wraps the TCP connection. The MLLP framing runs inside the encrypted tunnel. It works, but the tooling is thinner. Cloud load balancers can terminate HTTPS at the edge. Terminating MLLP TLS at the edge requires a TCP load balancer with TLS passthrough or a proxy that understands MLLP framing.
Mutual TLS (mTLS) is more common in MLLP than HTTP because MLLP interfaces are typically point-to-point between known systems. Both sides have certificates. HTTP APIs more commonly use bearer tokens or API keys for authentication, though mTLS is growing in service mesh environments.
Throughput
MLLP processes one message per connection at a time. To increase throughput, you open more connections. Ten connections can carry ten messages concurrently.
HTTP/1.1 has the same limitation per connection but supports connection pooling natively. HTTP/2 multiplexes multiple requests over a single connection, which is why it dominates high-throughput API communication.
For HL7 workloads, throughput is rarely the bottleneck. A busy hospital interface sends thousands of messages per day, not per second. Both protocols handle this comfortably. Throughput differences only matter for bulk data loads like historical data migration or batch reporting, where HTTP’s multiplexing has a measurable advantage.
When MLLP is the only option
- The sending system only speaks MLLP (most lab instruments, older HIS/RIS)
- The receiving system only speaks MLLP (legacy interface engines)
- The HL7 v2 interface specification mandates MLLP
- National or regional standards require MLLP for specific message types
When HTTP is the better choice
- Both sides support it (FHIR servers, modern EHRs, cloud services)
- You need URL-based routing, header-based authentication, or API gateway features
- The integration runs over the public internet (HTTPS is better supported)
- You want standard observability without custom instrumentation
When you need both
Many healthcare organizations run both protocols simultaneously. Inbound lab results arrive over MLLP from instruments. Outbound FHIR resources go over HTTP to a cloud data platform. The integration layer translates between them.
This is the most common real-world architecture: MLLP on the hospital side, HTTP on the cloud side, with something in the middle that speaks both.
For details on MLLP’s framing and connection model, see What is MLLP and how does it work. For the acknowledgment codes referenced in this article, see HL7 ACK and NAK codes: AA, AR, AE explained.