skip to content

What encodings does the SAML HTTP-Redirect binding apply to a message, and why does the result still overflow URLs?

level: middleimportance: must knowfreq 46%

answer

  1. compress before you encode
  2. raw DEFLATE, then base64, then URL
  3. each expansion fights the compression
  4. signed content barely compresses at all
  5. the shortest hop sets the URL ceiling

basics

~20 s

The message is DEFLATE-compressed, then base64-encoded, then URL-encoded into a SAMLRequest or SAMLResponse query parameter. Base64 adds about a third back and percent-encoding adds more, so anything carrying a signature and a certificate barely compresses and overflows.

solid answer

~40 s

The HTTP-Redirect binding uses the encoding named `urn:oasis:names:tc:SAML:2.0:bindings:URL-Encoding:DEFLATE`: DEFLATE per RFC 1951, then base64 per RFC 2045 with whitespace removed, then URL-encoding into the `SAMLRequest` or `SAMLResponse` query parameter. The optional `SAMLEncoding` parameter names the encoding in use, and its absence means this one. The pipeline works well on an `<AuthnRequest>`, which is small and highly repetitive XML. It works badly on a `<Response>` carrying a signed assertion, because a signature value and an embedded certificate are high-entropy and compress hardly at all, after which base64 adds roughly a third and percent-encoding adds more on top. That is why the binding specification prefers POST when a message contains signed content, and why the same integration can succeed for one counterparty and return 414 for another.

code

pseudocode · 6 lines
pseudocode
function encode_for_redirect(message_xml):
    deflated = deflate(message_xml)       // RFC 1951, raw, no wrapper
    encoded  = base64(deflated)           // RFC 2045, whitespace removed
    return url_encode(encoded)            // into SAMLRequest or SAMLResponse

// decode is the same three steps in reverse order

go deeper

for a junior

Remember the order — compress, base64, URL-encode — and that the message ends up in a SAMLRequest or SAMLResponse query parameter with no page shown to the user.

for a middle

Explain why the three steps fight each other: DEFLATE shrinks, base64 adds about a third, percent-encoding adds more. Then say which messages compress well and which do not, and why.

for a senior

Diagnose the length failure properly: measure the encoded parameter, find the smallest URL ceiling on the path, and recognise silent truncation showing up as a parse error rather than a transport error.

for a principal

The decision is which direction of each exchange runs on which binding across a partner estate you do not control, accepting one uniform choice over per-partner tuning you will have to remember years later.

## The pipeline, in order The HTTP-Redirect binding, `urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect`, has to fit a whole XML document into a URL. It does that with the encoding identified as `urn:oasis:names:tc:SAML:2.0:bindings:URL-Encoding:DEFLATE`, applied in a strict order: 1. **DEFLATE** the serialised XML, per **RFC 1951**. This is the raw compressed data, without a surrounding wrapper of the kind a file-compression format would add — a sender that emits a wrapped stream produces something the other side cannot inflate. 2. **Base64** the compressed octets, per **RFC 2045**, with any whitespace removed. RFC 2045 base64 permits line breaks; a URL does not want them. 3. **URL-encode** the result into the `SAMLRequest` or `SAMLResponse` query parameter, because base64's alphabet includes `+`, `/` and `=`, all of which have their own meaning in a query string. The browser then follows a redirect to that URL. There is no page and nothing for the user to click. An optional **`SAMLEncoding`** parameter names the encoding that was applied. When it is absent, the DEFLATE URL encoding above is what the recipient assumes, which is why almost nobody sends it. `RelayState`, if present, is a separate query parameter, and at the binding level its value must not exceed **80 bytes**. If there is nothing to carry, the parameter is omitted altogether rather than sent empty — a detail that matters more than it looks, because it changes the string a signature is computed over. ## Why the encoding fights itself The three steps do not pull in the same direction: - DEFLATE **shrinks**, and on XML it shrinks a great deal. A protocol message is mostly repeated element names, namespace declarations and attribute names, which is close to the ideal input for a dictionary compressor. - Base64 **grows** by roughly one third, because it re-expresses three octets as four characters. - URL-encoding **grows again**, unpredictably, because every reserved character becomes a three-character escape. So the question is never "does it compress?" but "does it compress faster than the two expansions grow it?" For an `<AuthnRequest>` the answer is comfortably yes. For a `<Response>` carrying a signed assertion the answer is usually no, and the reason is instructive: the bulk of that document is a base64 signature value and often a base64 certificate. Both are already high-entropy, so DEFLATE finds almost nothing to remove, and then the two expansion steps operate on nearly the original size. | Message | Typical shape | Compresses? | Usual binding | |---|---|---|---| | `<AuthnRequest>` | small, repetitive, no embedded key material | very well | Redirect is comfortable | | `<Response>` with a signed assertion | signature value plus an embedded certificate | barely | POST, or Artifact | | `<LogoutRequest>` | small, a subject identifier and a session reference | well | either | The binding specification makes exactly this point when it prefers POST for a message containing signed content: after encoding, the length essentially precludes the Redirect binding. Note the shape of that statement — it is a consequence of size, not a prohibition. Nothing rejects a long Redirect URL at the specification level. Something in the path rejects it at run time. ## Why it fails in one tenant and not another This is the leaf's signature production incident. The same integration, the same code, the same message type: one counterparty works and another returns 414 or silently truncates. The variables are all outside the specification. - **Message size differs by counterparty.** An extra attribute statement, a longer identifier, a chain of certificates rather than one, a localised display name — each adds bytes before compression. - **Length ceilings differ by hop.** Browsers, reverse proxies, load balancers, gateways and logging middleware each impose their own maximum URL length, and the effective limit is the smallest one on the path. A message that survives a direct connection can die behind an extra proxy. - **Truncation is worse than rejection.** A hop that cuts the URL rather than refusing it produces a base64 string that still decodes to something, which inflates to a partial document, which fails to parse — and the error surfaces as malformed XML, pointing the operator at the message rather than at the transport. The diagnostic reflex is to measure rather than guess: take the encoded parameter's length, compare it against the shortest ceiling on the path, and if the message carries signed content, move that direction of the exchange to HTTP-POST. Moving it is not a free swap, because it also moves where the signature lives — from a detached pair of query parameters to an enveloped signature inside the XML — but it removes the length budget from the picture entirely.

  • Why does an AuthnRequest fit comfortably while a Response usually does not?
    An `<AuthnRequest>` is small and highly repetitive, so DEFLATE removes most of it. A `<Response>` is dominated by a signature value and often an embedded certificate, both high-entropy, so compression gains little and the base64 and percent-encoding expansions then apply to nearly the full size.
  • What does the SAMLEncoding parameter do on a Redirect URL?
    It names the encoding applied to the message. When it is absent the recipient assumes `urn:oasis:names:tc:SAML:2.0:bindings:URL-Encoding:DEFLATE`, which is why it is rarely sent in practice.
  • The same integration works for one counterparty and returns 414 for another. Where do you look?
    Measure the encoded parameter's length for each, then find the smallest URL ceiling on each path — a proxy or gateway usually differs between them. If the failing direction carries signed content, move it to HTTP-POST rather than trying to shrink the message.

saying these in an interview costs you the question

  • Thinks DEFLATE makes any message short enough for a URL
  • Believes base64 makes the message smaller
  • Wraps the compressed stream in a file-format header
  • Assumes a URL that works on one path works on every path
  • Says moving a signed message to POST changes nothing else