How to escape Advanced Custom Fields output in your templates
Why get_field() returns unescaped data, which WordPress escaping function fits each field type, and examples you can copy.
Quick answer: ACF returns what an editor saved, so you must escape it when you print it. Use esc_html() for plain text, esc_url() for links, esc_attr() inside attributes and wp_kses_post() for rich text.
Why escaping matters
Field values are stored as typed. If a field contains markup or a script, printing it as is can break your layout or open the door to cross-site scripting. Escaping at the moment of output is the WordPress way to stay safe, even when you trust your editors.
Which function for which field
- Text, number, select:
esc_html( get_field( 'subtitle' ) ) - URL and link href:
esc_url( get_field( 'website' ) ) - Value inside an attribute:
esc_attr( get_field( 'css_class' ) ) - WYSIWYG and textarea with HTML:
wp_kses_post( get_field( 'content' ) ), which keeps safe tags and removes the rest. - Image: use the ID with
wp_get_attachment_image(), or escape the URL withesc_url().
An example with a link field
$link = get_field( 'cta' );
if ( $link ) {
printf( '<a href="%s" target="%s">%s</a>',
esc_url( $link['url'] ),
esc_attr( $link['target'] ?: '_self' ),
esc_html( $link['title'] )
);
}
Watch out for the_field()
the_field() prints the raw value without escaping. It is convenient, but use echo esc_html( get_field( ... ) ) instead when the data is not already safe.
Check before you print
An empty field should not leave empty tags on the page. Wrap output in an if, so missing values do not produce blank headings or broken links.
Generate it instead of typing it
The ACF code snippet generator gives you escaped, copy-ready code for each field type. Two more tools help with fields: the JSON to PHP converter and the location rules builder.