The primary culprit is a filter called wpautop that automatically applies to post and page content. The wpautop filter converts double line breaks to sets of opening and closing paragraph tags. It works fine if your posts are text only, contain no images, no short codes, and only the most basic inline markup. Throw a few block-level elements into a post or a shortcode or two, however, and watch your markup devolve into a tangled mess of improperly-nested tags.
How To Tell If Wpautop Is Causing Problems
You can usually tell if wpautop is causing HTML validation to fail if the W3C Validator is giving you the errors:
document type does not allow element "xxx" here
end tag for "xxx" omitted, but OMITTAG NO was specified
Seeing this combination of errors, particularly when the tag in question is a <p> tag, is a pretty good indication that wpautop is to blame.
The following are a few common solutions for living with, or without, wpautop in WordPress.
Turn it Off
If you are comfortable with HTML markup you may be better-served by just disabling wpautop completely and specifying all of your own HTML markup when writing blog posts. You can disable wpautop either by installing a plug-in that does it for you (this one for example), or by adding the following lines to your theme’s functions.php file:
remove_filter( 'the_content', 'wpautop' ); remove_filter( 'the_excerpt', 'wpautop' );
I like to use the wpautop-control plugin, which allows you to set wpautop to enabled or disabled by default, then switch it on or off on a post-by-post (or page-by-page) basis. This allows me to develop complex pages with wpautop disabled, but leave it enabled for simpler blog content created later by my clients.