Ten characters in XML break a URL
XML markup is built from the same characters a URL reserves for structure. An angle bracket opens a tag in one and confuses proxies in the other. An ampersand separates a child entity in one and starts a new query parameter in the other. Send raw XML through a query string and the server reads the first fragment, then treats everything after the first & as a different field.
| Character | Escape | What happens when it stays raw |
|---|---|---|
& | %26 | Starts a new query parameter. Half your document lands in a field the endpoint never reads. |
= | %3D | Splits a key from a value. An attribute assignment turns into a parameter boundary. |
# | %23 | Starts the fragment. Everything after it stays in the browser and never reaches the server. |
% | %25 | Opens an escape sequence. A literal percent leaves a malformed pair the decoder throws on. |
<> | %3C%3E | Forbidden in a URI by RFC 3986. Proxies, WAFs, and log parsers rewrite or reject them. |
" | %22 | Closes the quoted attribute when the URL is written into an HTML link. |
| space | %20 | Ends the request line as far as older servers are concerned. Indented XML is mostly spaces. |
+ | %2B | Reads as a space in form-encoded values, so a plus sign in your data silently disappears. |
| line break | %0A%0D | A raw CR LF inside a request line is the request-splitting pattern. Servers drop the request. |
Percent encoding replaces each of these with a percent sign and the hex value of the byte. Nothing about the XML changes. The bytes travel through the URL layer intact, and whatever sits on the other end decodes them back before parsing.
Three escaping rules, three different outputs
The Escaping control picks how aggressive the encoder gets. The same fragment produces three results, and picking the wrong one is the most common reason a request arrives mangled.
%3Cnote%20a%3D%221%22%3E
x%20%26%20y%3C%2Fnote%3E%3Cnote+a%3D%221%22%3E
x+%26+y%3C%2Fnote%3E%3Cnote%20a=%221%22%3E
x%20&%20y%3C/note%3EQuery value escapes every reserved character, including &, =, /, and ?. Use it whenever the XML sits inside one parameter. This is the setting you want almost every time.
Form field does the same work, then writes spaces as + instead of %20. That rule comes from HTML form submission, not from the URI specification. Some frameworks decode + as a space everywhere, some only in a POST body. Pick this when you are building a body with Content-Type: application/x-www-form-urlencoded.
Whole URI leaves the delimiters alone, so &, =, /, and # pass through untouched. Look at the third card above. The ampersand from x & y survives, which means the endpoint sees a new parameter starting mid-document. This setting exists for escaping a URL that is already assembled, not for escaping data going into one. Use it on a finished address, never on XML.
The Strict RFC 3986 box adds the six characters JavaScript leaves behind: !, ', (, ), and *. They are legal in a URL, so most endpoints never notice. AWS request signing, OAuth 1.0 signature bases, and a few payment gateways compute their hash over the strict form, so an unescaped apostrophe there produces a signature mismatch with no useful error message.
XML entities and percent encoding sit on different layers
Both look like escaping. They solve different problems, and mixing them is where the confusing bugs live.
An XML entity such as & or < belongs to the document. The XML parser resolves it. Percent encoding belongs to the transport. The HTTP stack resolves it. Load the Ampersands and entities sample and watch what happens to Smith & Sons. The ampersand of the entity becomes %26, so the encoded string holds %26amp%3B. Decoding restores &, the parser turns that into a literal ampersand, and the buyer name comes out right.
The failure mode is unescaping twice. Some code decodes the URL, sees text containing <, and runs an HTML entity decoder over it for safety. Now a customer note reading <script> has become a real tag, and the document either fails to parse or carries markup nobody intended. Decode the transport layer once, hand the bytes to the XML parser, and let the parser resolve the entities.
Double encoding, and how to recognise it
Encoding an already-encoded string turns every % into %25. The result looks plausible and travels fine. It breaks quietly at the far end, where the parser receives literal text like %3Cconfig%3E rather than markup.
%3Cconfig%3E%3Cmode%3E
live%3C%2Fmode%3E%253Cconfig%253E%253Cmode
%253Elive%253C%252Fmode%253EThe tell is %25 followed by two more hex digits. Decoding flags it here rather than handing back a half-readable string. Load the Encoded twice sample, press Decode, and the status line says the output still holds escapes. Decode again to reach the XML.
Double encoding usually comes from a client library that already escapes parameters. Building the query string by hand and then passing it to a helper such as requests in Python or URLSearchParams in the browser applies the second pass. Give those libraries the raw XML string and let them escape it once.
The 2,000 character line
HTTP itself sets no maximum URL length. Every implementation between you and the endpoint sets one anyway, and the smallest number in the chain wins.
- RFC 7230 asks servers to accept a request line of at least 8,000 octets. A floor, not a promise.
- Apache defaults
LimitRequestLineto 8,190 bytes. - nginx starts at a 8 KB header buffer through
large_client_header_buffers. - IIS ships with a 2,048 byte query string cap and a 4,096 byte URL cap.
- CDNs and WAFs sit in front of all of that and often cut lower, with a 414 status and no detail.
The URL budget meter measures against 2,000 characters, because that is the point below which nothing in the usual chain complains. Percent encoding roughly triples the length of tag-heavy XML, since every angle bracket costs three characters instead of one. A 700 byte SOAP envelope lands near the limit on its own.
Collapse whitespace between tags is ticked by default for that reason. Indentation carries no meaning between elements, and stripping it from a pretty-printed document removes a third of the length before encoding starts. Text inside an element stays untouched, so values keep their spacing.
Past 2,000 characters, stop encoding and change the request. A document too big for a query string is telling you it belongs in a POST body with Content-Type: application/xml, where no escaping happens at all. Query strings also land in server access logs, browser history, proxy caches, and the Referer header of the next request. XML holding customer records, tokens, or medical data does not belong in a URL at any length.
What the checks tell you
Every encode runs the source through the browser DOM parser first. A well-formedness failure does not block the encoding, because escaping a broken fragment is a reasonable thing to want. The status line says so instead, which saves the round trip of discovering it after the request returns a 400.
Decoding runs two checks of its own. A stray % without two hex digits behind it reports the exact position, since a missing %25 is the usual cause. A sequence such as %C0 or %E2%28 is valid hex but not valid UTF-8, which throws in every decoder including the one behind your endpoint. Both come back as a message rather than an empty box.
Unicode passes through as UTF-8 bytes. Load the Accents and emoji sample and the character count stays close to the source while the byte count jumps, because รถ costs two bytes and an emoji costs four. The byte figure is what a server counts against its limits.
Where this page stops
There is no compression step. Percent encoding grows a document, never shrinks one. Teams pushing large XML through a URL usually gzip the bytes and Base64 the result first, which needs matching code on both ends. The XML to Base64 page covers half of that.
Validation stops at well-formedness. Whether your document matches its XSD or WSDL contract is a separate question, and a schema check belongs before the encoding step rather than after it.
Nothing here splits a document across several parameters. Some APIs accept doc1, doc2, and so on, reassembled server side. That scheme has to be agreed with the endpoint, so building it is your side of the work.
Everything runs in this tab. The XML never reaches a server, which is what makes the page usable with a payload from production. Nothing survives closing the tab, so save what you need.
Nearby pages
For plain text rather than markup, the URL encoder skips the XML checks. When the document needs to travel in a header or a JSON string instead of a URL, XML to Base64 is the usual choice. If the XML refuses to parse before you get this far, XML parser reports the line and column, and XML pretty print re-indents a document flattened by the collapse option. A SOAP response that came back from the endpoint is easier to read through SOAP to JSON.
