Enqueue Hooks: Four Hooks, Four Different Zones — and How Not to Mix Them Up
On the surface, these five hooks all do the same thing — load CSS/JS. But four of them sound almost identical while each one runs in a different…

Filter Hook since 4.1.0
Filters the final <script> tag markup for an enqueued script
script_loader_tag is how you add async/defer or a nonce to a specific enqueued script without touching the theme's enqueue logic wholesale.
During wp_head/wp_footer script printing, once per enqueued script handle.
apply_filters( 'script_loader_tag', string $tag, string $handle, string $src );$tag string — The <script> tag HTML.$handle string — The script's registered handle.$src string — The script's source URL.add_filter( 'script_loader_tag', function( $tag, $handle ) {
if ( 'my-script' !== $handle ) {
return $tag;
}
return str_replace( ' src', ' defer src', $tag );
}, 10, 2 );Add the defer attribute to one specific enqueued script.

On the surface, these five hooks all do the same thing — load CSS/JS. But four of them sound almost identical while each one runs in a different…
No — only for scripts registered via wp_enqueue_script()/wp_register_script(). Manually echoed <script> tags aren't touched.
Source: wp-includes/class-wp-scripts.php
One session: this hook, then each related_hooks entry. Walk it and tell us what to change.
script_loader_tag → wp_enqueue_scripts
Practice the script_loader_tag WordPress filter hook in our interactive sandbox — a VS Code-style editor with a step-by-step script_loader_tag tutorial, add_filter exercises, and instant feedback. No local WordPress install required.