Hooks
questions
WordPress action and filter hooks – real questions from interviews, code reviews, and daily development.
Can wp_insert_comment fire without a real visitor ever submitting anything?
Yes — any code that calls wp_insert_comment() directly, including import tools, migration scripts, and other plugins, triggers this hook exactly as if a visitor had submitted the comment.
Do these five hooks fire in the order they’re registered in code?
No — they fire in a fixed pipeline order determined by the REST API’s own request lifecycle (rest_pre_dispatch → authentication → routing → rest_prepare_* → rest_insert_*), regardless of when your plugin registers its callbacks.
Does comment_approved always reflect a comment’s final status?
No — it reflects the moderation outcome at the moment comment_post fires. A comment can be manually approved or marked as spam afterward by a moderator, and that later change doesn’t re-fire comment_post.
Does rest_insert_post fire for posts saved through the normal editor, not the REST API?
No — it fires specifically for posts created or updated through a REST API request. A normal editor save fires wp_insert_post and save_post, not rest_insert_post.
Is sanitizing in comment_text enough to prevent stored XSS from comments?
No. comment_text only affects what’s displayed at render time — the raw value is already stored in the database by the time this filter runs. Real protection has to happen at submission, before the comment is inserted.
Should I use rest_prepare_post or register_rest_field to add a computed field to the response?
register_rest_field for a straightforward computed value tied to the schema. rest_prepare_post when you need to both add and remove fields, or when the logic depends on the full response object rather than a single field’s value.
What’s the difference between comment_post and wp_insert_comment?
comment_post fires specifically when a comment is submitted through the standard front-end comment form. wp_insert_comment fires for every comment insert regardless of source — front-end submissions, programmatic inserts, imports — so it’s the one to use when you need to catch all of them.
What’s the difference between rest_pre_dispatch and rest_authentication_errors for blocking a request?
rest_pre_dispatch can short-circuit a request entirely, before routing, for conditions that apply broadly (maintenance mode, global rate limits). rest_authentication_errors is specifically about identity and access — and it can undo another check’s rejection if you don’t respect an existing non-null $result.
When should a dedicated team review comment handling on a site?
Before the volume of user-generated content makes manual review impractical — a community site, a membership platform, or any site where comments are a primary engagement feature benefits from a security pass on the full submission pipeline, not just the rendered output.
When should a dedicated WordPress team review a site’s REST API hook usage?
When multiple plugins or custom code hook into authentication and response-shaping on the same endpoints — at that point, tracing which check actually wins requires understanding the full pipeline order, which is exactly the kind of audit worth outsourcing rather than reconstructing from scratch under time pressure.
Why did my rest_authentication_errors check stop another plugin’s block from working?
Almost certainly because the filter didn’t check whether $result was already non-null before returning its own value — overwriting an existing WP_Error (or a success) with your own default silently replaces the other check’s decision.
Why does comment_text run more than once for the same comment?
Because it’s an output filter that runs every time the comment is rendered, not once when it’s created. The same comment shown on a paginated list and again on its own permalink page runs the filter twice, independently.
Hook actions
Can I cancel a deletion from inside delete_attachment?
No — this action fires as notification, not permission. It can't stop the delete from proceeding.
Used in
delete_attachment Hook action
Can I read the image dimensions inside add_attachment?
Not yet — metadata generation happens afterward. Hook wp_generate_attachment_metadata if you need sizes or EXIF data.
Used in
add_attachment Hook action
Can I redirect the user somewhere custom after logout?
Yes — hook wp_logout (or the logout_redirect filter for more control over the target URL) and call wp_safe_redirect() followed by exit.
Used in
wp_logout Hook action
Can I redirect users away from wp-admin on admin_init?
Yes, that's a very common pattern — check current_user_can() and wp_redirect() + exit if the check fails, being careful to allow admin-ajax.php requests through.
Used in
admin_init Hook action
Can I use after_setup_theme in a plugin?
Yes, but it's primarily meant for themes. Plugins usually use plugins_loaded or init instead.
Used in
after_setup_theme Hook action
Can I use functions from my regular plugins inside a muplugins_loaded callback?
No — regular (non-mu) plugins have not loaded yet at this point. That is exactly what plugins_loaded is for.
Used in
muplugins_loaded Hook action
Do I need this if I already hook save_post?
Only if you need the REST request object itself — headers, route, or client-specific data that save_post doesn't expose.
Used in
rest_insert_post Hook action
Do I need to call wp_footer() myself in a block theme?
No — a block (FSE) theme has no footer.php to edit. WordPress calls wp_footer() automatically after the block template renders, so anything hooked here still prints right before </body>.
Used in
wp_footer Hook action
Does add_attachment fire for REST API uploads too?
Yes — it fires from wp_insert_attachment() regardless of whether the upload came from wp-admin, the REST API, or a programmatic call.
Used in
add_attachment Hook action
Does before_delete_post fire when I click ‘Trash’ on a post?
No. Moving a post to Trash fires wp_trash_post instead. before_delete_post only fires for a permanent/hard delete — either from Trash or via force-delete.
Used in
before_delete_post Hook action
Does edit_attachment fire when I update media via the REST API?
No — that path fires rest_insert_attachment instead. edit_attachment is specific to the classic wp-admin edit form.
Used in
edit_attachment Hook action
Does enqueue_block_editor_assets also run on the front-end for block themes (FSE)?
No — this hook is editor-only, even in a block theme. For assets needed both in the editor and on the front-end (e.g. a block's own styling), use enqueue_block_assets instead.
Used in
enqueue_block_editor_assets Hook action
Does muplugins_loaded fire if I have no mu-plugins at all?
Yes — WordPress calls this hook unconditionally on every request, whether the mu-plugins folder has files in it or not.
Used in
muplugins_loaded Hook action
Does rest_insert_post replace save_post for REST-created posts?
No — both fire. rest_insert_post adds the request context on top of what save_post already gives you.
Used in
rest_insert_post Hook action
Does user_register fire for users created in wp-admin by an administrator?
Yes — any path that goes through wp_insert_user() triggers it, including admin-created accounts and REST API user creation.
Used in
user_register Hook action
Does wp_head still fire in a block theme (FSE), since there’s no header.php?
Yes. WordPress auto-inserts wp_head()/wp_footer() around the block-based template output, so every callback you register still runs — you just never see or edit the call site yourself.
Used in
wp_head Hook action
Does wp_login fire for REST API or application password logins?
No — it fires only for the standard wp_signon() cookie-based login flow, not REST authentication.
Used in
wp_login Hook action
How do I detect only the first publish, not every re-save?
Check that $new_status === 'publish' && $old_status !== 'publish' inside transition_post_status.
Used in
transition_post_status Hook action
How do I find the right $hook_suffix value?
Add error_log($hook_suffix) inside the callback and check your PHP error log while on the target screen, or use get_current_screen()->id.
Used in
admin_enqueue_scripts Hook action
How do I make an admin_notices message dismissible?
Add the is-dismissible CSS class to the notice div and enqueue wp-admin's built-in dismiss script, or track dismissal yourself via user meta.
Used in
admin_notices Hook action
How often does wp_scheduled_delete run?
Once daily, as one of WordPress's built-in scheduled events.
Used in
wp_scheduled_delete Hook action
Is it safe to run heavy logic directly inside save_post on a large site?
Not if it's synchronous — save_post runs inside the save request itself, so slow logic on a large database can lock tables and delay the editor's save response. Offload it to a queue or scheduled event (e.g. Action Scheduler or wp_schedule_single_event) instead.
Used in
save_post Hook action
Is plugins_loaded too early for register_post_type?
Yes for some setups — use init instead; plugins_loaded is for dependency checks, not registering WordPress objects.
Used in
plugins_loaded Hook action
Is the file still on disk when this fires?
Yes — the file and its generated sizes are still present; this is the last point where get_attached_file() returns a valid path.
Used in
delete_attachment Hook action
Is wp_enqueue_scripts the right hook for loading scripts inside the block editor?
No — that's enqueue_block_editor_assets (editor only) or enqueue_block_assets (editor + front-end). wp_enqueue_scripts only runs for the public front-end.
Used in
wp_enqueue_scripts Hook action
Should block CSS go on enqueue_block_assets or block.json’s style property?
block.json's style/editorStyle properties are the modern, preferred way for a registered block's own CSS. enqueue_block_assets is better for shared/global styling that isn't tied to one specific block registration.
Used in
enqueue_block_assets Hook action
Should I enqueue scripts in wp_head?
No. Hook wp_enqueue_scripts, then let core print the tags during wp_head.
Used in
wp_head Hook action
Should I use wp_insert_post or save_post?
Use save_post for editor-driven saves; use wp_insert_post if you also need to catch posts created programmatically outside the editor.
Used in
wp_insert_post Hook action
What does $comment_approved actually contain?
1 for approved, 0 for pending moderation, or the string 'spam' — check it before triggering notifications so you don't email admins about spam.
Used in
comment_post Hook action
What’s different between edit_attachment and save_post here?
save_post also fires for attachments since they're a post type, but edit_attachment is scoped specifically to the dedicated media-edit admin screen.
Used in
edit_attachment Hook action
What’s the difference between init and wp_loaded?
init runs first and is where you register things; wp_loaded runs after all init callbacks finished, so it's safer for code that reads what other plugins registered.
Used in
wp_loaded Hook action
Why did my script load but not run in the right order?
Set proper dependency arrays (e.g. ['jquery']) in wp_enqueue_script() instead of relying on hook priority to control load order.
Used in
wp_enqueue_scripts Hook action
Why did my widget’s post list get shortened after I used pre_get_posts?
You likely modified every WP_Query, not just the main one. Add $query->is_main_query() to the condition.
Used in
pre_get_posts Hook action
Why didn’t this fire at exactly midnight?
WP-Cron is pseudo-cron — it only checks for due events on incoming site requests, so timing depends on traffic unless a real system cron drives it.
Used in
wp_scheduled_delete Hook action
Why does register_rest_route() have to run on rest_api_init specifically?
The REST server object doesn't exist yet on earlier hooks like init; rest_api_init is where WordPress builds it before dispatching the request.
Used in
rest_api_init Hook action
Why does save_post fire twice?
Once for the autosave/revision and once for the real post. Guard with wp_is_post_autosave() and wp_is_post_revision().
Used in
save_post Hook action
Why doesn’t is_page() work in my redirect on init?
The main query hasn't executed yet on init. Use template_redirect, where the query is finalized and conditional tags are reliable.
Used in
template_redirect Hook action
Why doesn’t my admin page show up?
add_menu_page() must be called on admin_menu, not admin_init or plugins_loaded — those run before the menu system is ready.
Used in
admin_menu Hook action
Why doesn’t my sidebar show up in the widgets screen?
register_sidebar() must run on the widgets_init hook — calling it earlier or later means WordPress never registers the widget area.
Used in
widgets_init Hook action
Why is my analytics snippet blocking page render?
Large synchronous <script src> tags echoed in wp_footer still delay paint — add the async/defer attribute instead of a raw echoed tag.
Used in
wp_footer Hook action
Why is my custom post type missing from the admin menu?
It usually means register_post_type() ran on a hook earlier than init, or the CPT registration code never executed on that request.
Used in
init Hook action
Why is my site slow on every page, not just one template?
init fires on every request type. Guard expensive logic with is_admin(), wp_doing_ajax(), or wp_doing_cron() so it only runs where it's actually needed instead of on every hit.
Used in
init Hook action
wp_insert_comment vs comment_post — which should I use?
comment_post is specific to the standard front-end submission flow and gives you the approval status directly. wp_insert_comment fires for every insert path (including REST/import) and gives you the full WP_Comment object.
Used in
wp_insert_comment Hook action
Hook filters
Can I hide the admin footer text entirely?
Return an empty string from the filter — WordPress will render the footer area with no text.
Used in
admin_footer_text Hook filter
Can I remove a default body class instead of adding one?
Yes — search $classes with array_search() and unset() the key, then return the array.
Used in
body_class Hook filter
Do I still need wp-login.php to exist after filtering login_url?
Only if something still links to it directly. This filter changes what wp_login_url() outputs; it doesn't disable the real wp-login.php endpoint.
Used in
login_url Hook filter
Does an attachment post exist when this filter runs?
No — this fires before wp_insert_attachment() creates the post, so there's no attachment ID available yet.
Used in
wp_handle_upload Hook filter
Does changing wp_mail_from fix WordPress emails going to spam?
It helps, but deliverability mainly depends on SPF/DKIM/DMARC records and your mail sending method — this filter alone won't fix a misconfigured domain.
Used in
wp_mail_from Hook filter
Does filtering comment_text change the comment stored in the database?
No — it only changes what's rendered on display. The database row is untouched.
Used in
comment_text Hook filter
Does lowering this threshold affect already-uploaded images?
No — it only affects images processed after the filter is added; existing attachments aren't retroactively rescaled.
Used in
big_image_size_threshold Hook filter
Does render_block run for classic-editor content?
No — only for actual blocks. Classic editor HTML that isn't parsed into blocks goes through the_content filters instead.
Used in
render_block Hook filter
Does script_loader_tag work for scripts I just echo manually?
No — only for scripts registered via wp_enqueue_script()/wp_register_script(). Manually echoed <script> tags aren't touched.
Used in
script_loader_tag Hook filter
Does the_excerpt run for manually written excerpts too?
Yes — it runs regardless of whether the excerpt was typed in the editor or auto-generated from content.
Used in
the_excerpt Hook filter
Does this filter affect images pasted as raw HTML in the editor?
No — only images rendered through WordPress's own attachment image functions (wp_get_attachment_image and friends), not raw <img> HTML.
Used in
wp_get_attachment_image_attributes Hook filter
Does this filter disable WP-Cron?
No — set the DISABLE_WP_CRON constant to true for that. This filter only adjusts the self-spawned HTTP request's arguments.
Used in
cron_request Hook filter
Does this run on every page load for existing images?
No — only when metadata is generated or explicitly regenerated (new upload, or a 'Regenerate Thumbnails' action), not on normal display.
Used in
wp_generate_attachment_metadata Hook filter
How do I reject a login attempt from this filter?
Return a WP_Error object. Returning null lets WordPress continue checking other registered authenticate callbacks instead of stopping the login outright.
Used in
authenticate Hook filter
How do I target only one specific menu?
Check $args->theme_location (or $args->menu) inside the callback and return $items unchanged for every other menu.
Used in
wp_nav_menu_items Hook filter
I added a column but the cells are blank — what’s missing?
manage_posts_columns only adds the header. You also need add_action( 'manage_posts_custom_column', … ) to echo the actual value for each row/column combination.
Used in
manage_posts_columns Hook filter
Is it safe to allow SVG uploads with this filter alone?
No — pair it with an SVG sanitizer library or plugin. The raw filter only changes what's accepted, not what's safe.
Used in
upload_mimes Hook filter
Is the wp_title filter deprecated?
The wp_title() template tag is legacy — modern themes render titles via wp_get_document_title() and the document_title_parts filter. wp_title only fires if a theme still calls wp_title() directly in header.php.
Used in
wp_title Hook filter
Is this the right hook to build a headless auth layer?
Yes — it's the standard extension point for custom REST authentication schemes like API keys or signed requests.
Used in
rest_authentication_errors Hook filter
post_class vs body_class — which one do I use?
body_class targets the <body> tag once per page; post_class targets each individual post wrapper and runs once per post in the loop.
Used in
post_class Hook filter
rest_prepare_post vs register_rest_field — which should I use?
register_rest_field is the documented way to add a field with schema support; rest_prepare_post is a lower-level filter for quick tweaks or removing/reshaping existing fields.
Used in
rest_prepare_post Hook filter
template_include vs single_template — which should I use?
single_template (and its siblings like page_template) only affect one specific query type; template_include is the catch-all that runs regardless of query type.
Used in
template_include Hook filter
What does returning null mean versus WP_Error?
null means 'no opinion, defer to other checks'; a WP_Error means 'block this request with this specific error'.
Used in
rest_authentication_errors Hook filter
What happens if I forget to return the metadata array?
The attachment loses its generated sizes and dimension data — always return $metadata, even unmodified.
Used in
wp_generate_attachment_metadata Hook filter
What’s the default threshold?
2560 pixels on the long edge, introduced in WordPress 5.3.
Used in
big_image_size_threshold Hook filter
What’s the difference between ‘upload’ and ‘sideload’ context?
'upload' is a direct user file upload; 'sideload' is a file pulled in programmatically, such as an image imported from a URL.
Used in
wp_handle_upload Hook filter
What’s the difference between rest_pre_dispatch and a route’s permission_callback?
permission_callback runs per-route after matching, for auth checks on that specific endpoint. rest_pre_dispatch runs before routing even happens and can intercept every request site-wide.
Used in
rest_pre_dispatch Hook filter
Why did my content disappear?
A the_content callback must return the string. Echoing instead of returning wipes the post body.
Used in
the_content Hook filter
Why did my excerpt_length change do nothing?
The post already has a manually written excerpt, or the theme calls a hardcoded wp_trim_words() instead of get_the_excerpt() — this filter only affects the auto-generation path.
Used in
excerpt_length Hook filter
Why did the_title also change my menu item labels?
The filter runs anywhere get_the_title()/the_title() is called, including some menu-label code paths. Check the post type or scope the condition more tightly.
Used in
the_title Hook filter
Why does my custom WP-Cron interval never fire?
Usually cron_schedules wasn't hooked before wp_schedule_event() ran, or the interval key name doesn't match exactly — WordPress silently ignores unknown interval names instead of throwing an error.
Used in
cron_schedules Hook filter
Why does the_content contain HTML comments like ?
Those are block delimiters the block editor saves into post_content. Each one already passed through the render_block filter before reaching the_content, so by the time your filter runs it's rendered HTML with block comments still attached — not raw block JSON.
Used in
the_content Hook filter
Why doesn’t excerpt_more change my manual excerpt?
It only applies to auto-generated excerpts. A manually written excerpt is used exactly as typed, with no trailing string appended.
Used in
excerpt_more Hook filter
Why doesn’t my wp_title filter work on this theme?
Modern themes render the title via wp_get_document_title(), which uses document_title_parts — wp_title() usually isn't called at all.
Used in
document_title_parts Hook filter
Why would I need to touch this on a high-traffic site?
High-traffic sites often disable the default request-triggered cron and drive wp-cron.php via a real system cron instead — this filter is mostly relevant when you still rely on WordPress's own self-ping.
Used in
cron_request Hook filter
No questions found. Try another search.
Smart FAQ Management
4WP FAQ
Not just another FAQ block. A smart wrapper that adds intelligence
without breaking your design.