Attachments and binary data: MTOM and SwA

Base64 inside the envelope is simple and wasteful. MTOM keeps the XML readable and sends the bytes as raw binary alongside it.

Three ways to send bytes

ApproachOn the wireOverheadInteroperability
Inline base64Text inside the bodyAbout 33 percent larger, and it all sits in memoryUniversal
SwA (SOAP with Attachments)A MIME multipart with the XML in part 0LowPoor; WS-I excluded it
MTOM with XOPA MIME multipart where the XML has xop:Include referencesLowGood on modern stacks
Out of bandA URL in the body, bytes fetched separatelyLowest for the messageBest for large files, but the link must be secured
<!-- MTOM: the XML stays the message, the bytes are referenced -->
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
  <soap:Body>
    <Upload xmlns="urn:example:files">
      <name>scan.pdf</name>
      <content>
        <xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include"
                     href="cid:[email protected]"/>
      </content>
    </Upload>
  </soap:Body>
</soap:Envelope>

<!-- The multipart body then contains:
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
Content-ID: <[email protected]>

...raw PDF bytes... -->
Content-Type: multipart/related; type="application/xop+xml";
              start="<[email protected]>"; start-info="application/soap+xml";
              boundary="MIME_boundary"

Practical rules

  • Declare the binary element as xs:base64Binary in the schema; the MTOM optimisation is then applied by the framework and does not change the contract.
  • Enable MTOM on both ends. A server sending XOP to a client that expects base64 fails at parse time with a confusing error.
  • Stream, do not buffer. A service that reads a 200 MB attachment into a byte array will fail under concurrent load long before the network is the problem.
  • Set an explicit maximum message size and reject early with a fault, rather than letting the container buffer until it dies.
  • Watch the SOAP engine's own limits: many have a default maximum message size in the low megabytes.
  • For files above a few tens of megabytes, prefer a pre-signed upload URL in the body and keep the SOAP message small.
// Enable MTOM on a WCF-style binding and stream the body
var binding = new BasicHttpBinding
{
    MessageEncoding = WSMessageEncoding.Mtom,
    TransferMode = TransferMode.Streamed,
    MaxReceivedMessageSize = 64L * 1024 * 1024,
    ReaderQuotas = { MaxArrayLength = 64 * 1024 * 1024 }
};
// TransferMode.Streamed matters more than the size limits:
// it keeps the payload off the managed heap.
💡
MTOM is an encoding detail, not a contract change. If you find yourself editing the WSDL to add an attachment, you have probably chosen SwA rather than MTOM, and interoperating with the other vendor's toolkit will be painful.

FAQ

Can I mix inline base64 and MTOM in one service?
Yes, per operation, and it is worth doing: small payloads avoid the multipart overhead while large ones stream. Document which operations use which.
Why is compression not applied automatically?
Binary formats such as PDF and JPEG are already compressed, so gzip wastes CPU for no gain. Compress text or XML attachments only, and always agree the encoding with the partner.

XML Schema and data types in messages Interoperability between .NET, Java and legacy stacks

Last refreshed 2026-09-18.