惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

U
Unit 42
T
The Blog of Author Tim Ferriss
H
Help Net Security
博客园 - 叶小钗
云风的 BLOG
云风的 BLOG
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
D
DataBreaches.Net
博客园 - 聂微东
A
About on SuperTechFans
大猫的无限游戏
大猫的无限游戏
P
Proofpoint News Feed
Martin Fowler
Martin Fowler
博客园 - 【当耐特】
S
SegmentFault 最新的问题
Blog — PlanetScale
Blog — PlanetScale
酷 壳 – CoolShell
酷 壳 – CoolShell
G
Google Developers Blog
I
InfoQ
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
GbyAI
GbyAI
B
Blog
Engineering at Meta
Engineering at Meta
V
V2EX
Hugging Face - Blog
Hugging Face - Blog

Lobsters

CIFSwitch: a non-universal Linux local root vulnerability RIPE NCC session fixation: poaching logins with an Atlas probe GNOME 2.20 but its Web Components Agentic Search for Context Engineering – Leonie Monigatti Garnix is shutting down [not OC] akashina.tngl.sh/jjc Concerning Emacs (and Jazz) Nitpicking the shell history scene in ‘Tron: Legacy’ What's cooking on SourceHut? Q2 2026 The tenth OpenPGP email summit Package managers that package package managers Clojure on Fennel part three: parsing WordPress at 23 Finding Miscompiles for Fun, Not Profit GitHub - creusot-rs/creusot: Creusot helps you prove your Rust code is correct. Announcing Rust 1.96.0 | Rust Blog A Love Letter to Neovim sqlite AGENTS.md Am I a Bad Friend? CSS vs. JavaScript • Josh W. Comeau Erlang Ecosystem Foundation - Supporting the BEAM community A brief note about slot access cost in Common Lisp Keyboard latency probe Rethinking the GNOME clipboard issues Back to the Building Blocks’ Building Blocks Tech Notes: Theseus: translating win32 to wasm Fast is better than slow Content-addressed Rust builds (or, what kache actually caches) Intent to Prototype: Embedding API Canada’s Bill C-22 and the security cost of collecting more data
Avoid using "<![CDATA[ ... ]]>" in RSS
https://waspdev.com/about · 2026-05-11 · via Lobsters

Published on
Updated on

<![CDATA[ ... ]]> is very commonly used in RSS (also Atom) feeds to escape XML special characters. At first glance, it looks very convenient, you simply add <![CDATA[ ... ]]> blocks and write any (almost) content inside of them without worrying about escaping characters:

		<item>
	<title><![CDATA[Using <CDATA> in Titles]]></title>
	<link>http://example.com</link>
	<description>
		<![CDATA[
			<p>This description contains <strong>HTML markup</strong>.</p>
			<p>It allows us to use characters like "<b>&</b>" and brackets directly.</p>
		]]>
	</description>
</item>

Why not CDATA?

CDATA seems to be perfect, isn't it? Except it's not possible to escape some CDATA special character sequences inside a single CDATA block, particularly ]]> (the one that ends the CDATA block). In order to do that, you have to split the CDATA block into multiple parts:

<text>
	<![CDATA[hello ]]]]><![CDATA[> world]]>
</text>

The encoded text is "hello ]]> world". As you can see, the XML code is less readable now. CDATA loses most of its simplicity advantage.

Even though splitting makes the encoding of ]]> possible, I would say it's still not worth using CDATA:

  • It adds a special edge case for ]]>, which the serializer must handle.
  • It can mislead people into thinking the content is raw HTML or somehow safer. No, it is not. Also, this might create a false sense of security in inexperienced people, which could even lead them to overlook ]]> (especially considering the rarity of ]]>).
  • It makes output less uniform, because sometimes you need split CDATA blocks.
  • It does not change the parsed value. XML parsers expose the same text either way.
  • It can make debugging confusing, especially if the content itself discusses CDATA, like this article title does... Just look at the RSS feed of this blog and see that it just escapes XML characters.

What to do instead?

Just escape these characters (works for HTML too):

function xmlEscape(text) {
	return text
		.replaceAll("&", "&amp;")
		.replaceAll("<", "&lt;")
		.replaceAll(">", "&gt;")
		.replaceAll('"', "&quot;")
		.replaceAll("'", "&#39;");
}

Normal escaping is simpler and more uniform.

OK, but some people might say that CDATA might make the RSS content smaller on average since characters don't need any escape (which requires more characters in encoded form) and ]]> is encountered rarely. Fair point, however:

  • Feeds are usually gzip-compressed. Repeated strings like &lt;, &gt;, and &amp; compress very well.
  • RSS feed size is rarely the bottleneck. Images, HTML pages, CSS, JS, and network latency usually matter much more.
  • CDATA has a special edge case. You still need to correctly handle ]]>.
  • Normal escaping is simpler and more uniform. One escaping path works for titles, descriptions, Atom, RSS, attributes, metadata, etc.

Conclusion

Here I listed the reasons why you should avoid using CDATA. This is especially true if you are going to implement your custom RSS / Atom feed generator. Many libraries / frameworks / CMSs still generate CDATA for RSS / Atom feeds and many of them handle the mentioned character sequence ]]> in their own ways. And they are perfectly fine to use if you have to rely on them. CDATA is common because it is convenient for legacy feed generators and visually cleaner for embedded HTML. But for new code, ordinary XML escaping is usually cleaner and more uniform.

See you later.