If you’ve spent any time building WordPress themes or plugins, you’ve definitely been in this frustrating situation: “My code is perfect. Why on earth isn’t this function running?”
Most of the time, your logic isn’t the problem—your hook timing is. WordPress relies entirely on an event-driven architecture (actions and filters). If you attach your code to the wrong hook, or set the wrong priority, things break. Fast.
Today, we’re looking at the 5 most common WordPress hook mistakes developers make, and how to fix them. Best of all? At the end of this post, I’ve included a custom WordPress hook order checker tool so you never have to guess when a hook fires again.
1. Setting Cookies or Redirects on init
It’s tempting for newer developers to just throw everything onto the init hook. But if you try to trigger a page redirect or set a cookie here, you’ll often get slapped with the dreaded “Headers already sent” error.
- The Mistake: Hooking redirects at
init. - The Fix: Whenever you need to redirect a user or set cookies, hook into
template_redirectinstead. At this point, WordPress has loaded the core configurations, but hasn’t started sending output to the browser yet.
2. The save_post Infinite Loop (A Server Killer)
When you use the save_post hook to modify post data and then call wp_update_post inside your function, guess what happens? wp_update_post triggers save_post all over again. Congratulations, you just built an infinite loop that will crash your site.
- The Fix: Before you update the post, remove your hook using
remove_action(). Run your update, and then reattach it withadd_action(). This breaks the cycle.
3. Forgetting Logged-out Users in AJAX
When handling custom AJAX calls outside the dashboard, developers often just register wp_ajax_{action} and call it a day. Then they wonder why the form works fine when they are logged in, but fails completely for site visitors.
- The Fix: Always register
wp_ajax_nopriv_{action}alongside your main hook if you want guest users to actually be able to run that AJAX function.
4. Enqueuing Scripts on admin_init
It sounds logical, right? You want to load CSS or JS in the admin area, so you use admin_init. Unfortunately, this is unpredictable and can cause your assets to leak into screens where they don’t belong, causing conflicts.
- The Fix: Always use the
admin_enqueue_scriptshook for backend assets. It even passes a$hook_suffixparameter, letting you limit your scripts to very specific admin pages.
5. Not Checking the Query on pre_get_posts
If you want to modify the main query on the frontend (like changing the number of posts per page), pre_get_posts is your best friend. But if you don’t use the is_main_query() check, your filter will accidentally apply to sidebars, navigation menus, and even admin dashboard queries.
- The Fix: Always start your custom query logic with a safety check:
if( ! $query->is_main_query() || is_admin() ) return;
🚀 Try the WordPress Hook Order Checker Tool
To figure out exactly which hook runs first and where you should inject your code, I built a completely free, interactive tool for the community. Using this WordPress hook order checker, you can visually track when hooks fire across Core WordPress, WooCommerce, and ACF. No more guessing.
👉 Click Here To Use The Tool in Full Screen
Or try it directly below:
Pick a request type, see the real core hook sequence, then drop your own function into the timeline to see exactly where it runs relative to WordPress core — including WooCommerce and ACF.
Bookmark this page for the next time you’re stuck on a weird hook execution sequence. Happy coding!