By default, WordPress strips most HTML from category and tag descriptions when you save them. Only a short list of inline tags survives, so headings, lists, images and paragraphs with classes disappear the moment you click Update.
That gets in the way when you use category pages as landing pages with real intro content, which is one of the easier ways to optimize category pages for SEO. The snippet below allows HTML in category, tag and custom taxonomy descriptions.
The Code
Add this code to your child theme’s functions.php file or to a custom plugin:
/**
* Allow HTML in term (category, tag) descriptions
*/
foreach ( array( 'pre_term_description' ) as $filter ) {
remove_filter( $filter, 'wp_filter_kses' );
if ( ! current_user_can( 'unfiltered_html' ) ) {
add_filter( $filter, 'wp_filter_post_kses' );
}
}
foreach ( array( 'term_description' ) as $filter ) {
remove_filter( $filter, 'wp_kses_data' );
}
How It Works
WordPress sanitizes term descriptions with two filters, and the snippet removes both. Learn more about adding and removing filters in WordPress Hooks Explained.
pre_term_descriptionruns before the description is saved. Core attacheswp_filter_kseshere, which strips everything except a few basic tags.term_descriptionruns when the description is displayed. In the admin only, core addswp_kses_data, which would strip the HTML again in the terms list table.
The current_user_can( 'unfiltered_html' ) check keeps it safe. Users with that capability can save any HTML, just like in the post editor.
Everyone else who can edit terms gets wp_filter_post_kses, the allowlist WordPress uses for post content. Tags like <h2>, <ul> and <img> work but <script> doesn’t.
Don’t remove wp_filter_kses without adding the fallback. That would let any user who can edit terms save scripts that run for every visitor.
What Changes After You Add It
The filters apply to every taxonomy, so the snippet covers categories, tags, custom taxonomies and WooCommerce product categories.
| Situation | Allowed HTML |
|---|---|
| Default, all users | a, abbr, acronym, b, blockquote, cite, code, del, em, i, q, s, strike, strong |
With the snippet, users with unfiltered_html |
Any HTML |
| With the snippet, other users | The post content allowlist |
Keep three things in mind:
- Old descriptions aren’t restored. HTML was stripped before it reached the database, so re-enter it after adding the code.
- On multisite, only super admins have
unfiltered_html. Site admins get the post allowlist. The same applies to everyone ifDISALLOW_UNFILTERED_HTMLis set inwp-config.php. - Your theme has to print the description.
term_description(),the_archive_description()and the Term Description block all output the stored HTML. A theme that escapes the description will show the tags as text.
If you’re building your own taxonomies, see how to create and query custom taxonomies. The wp_filter_kses() reference documents the sanitizer this snippet replaces.
FAQs
Common questions about HTML in category and tag descriptions:
wp_filter_kses on pre_term_description for every user, including administrators. It's a security default, and it only keeps basic inline tags such as links, bold and italics.
pre_term_description and term_description run for every taxonomy, including product_cat and product_tag. On product archives WooCommerce prints the description through wp_kses_post(), so scripts are removed there even if an admin saved them.