WordPress: Layout Broken by Unexpected HTML Tags in ACF Fields

A layout looks perfect on localhost but is broken on the staging or production server. The HTML source looks valid. Git is in sync. The culprit is often invisible: a content editor copied text from another website and pasted hidden HTML tags — <div> wrappers, inline styles, or empty <p> elements — directly into an ACF WYSIWYG field.

Problem: A layout works correctly on localhost but is broken on staging or production — inspecting the HTML source reveals that an ACF field is outputting unexpected <div> wrappers, inline styles, or empty <p> tags pasted from another website.

Solution: Sanitise ACF field output with strip_tags() when the field should contain plain text only, or use wp_kses() with an allowed-tag whitelist when limited HTML is acceptable. For WYSIWYG fields, set the toolbar to "Basic" to prevent rich formatting.

The simplest fix is strip_tags(), which removes all HTML entirely. Use it when the field should contain plain text only:

// Remove all tags — safe for plain-text fields
echo strip_tags( get_field( 'content' ) );

When you need to preserve some tags (e.g. <a>, <strong>, <em>) but strip unwanted wrappers, use wp_kses():

$allowed = [
    'a'      => [ 'href' => [], 'title' => [], 'target' => [] ],
    'strong' => [],
    'em'     => [],
    'br'     => [],
];
echo wp_kses( get_field( 'content' ), $allowed );

To strip specific tags using regex (for example, remove all <div> wrappers and empty <p> tags while keeping everything else):

function strip_layout_tags( $content ) {
    // Remove opening and closing div tags
    $content = preg_replace( '/<div[^>]*>/', '', $content );
    $content = preg_replace( '/<\/div>/', '', $content );

    // Remove empty paragraph tags (whitespace-only content)
    $content = preg_replace( '/<p[^>]*>\s*<\/p>/', '', $content );

    return $content;
}

echo strip_layout_tags( get_field( 'content' ) );

NOTE: The cleanest long-term solution is to sanitise field output at the ACF level using the acf/format_value filter, so every call to get_field() returns clean content automatically — without having to remember to wrap every output call in a sanitisation function.