A hook that fires before wp_head() on the front end?

I am looking for a hook that fires before a specific page is loaded on the front end.
So something before wp_head() if possible?
I’d be very appreciative of any suggestions.
Thank you.

EDIT: I am considering template_redirect()
Any feelings on the pros / cons?

1 Like

Look at action: https://developer.wordpress.org/reference/hooks/template_redirect/

or filter: https://developer.wordpress.org/reference/hooks/template_include/

2 Likes

Thanks, Joy.

Going to read through the documentation now.

I would like to do an additional authorization check for specific pages before any data on the page is sent to the user.
Would template_redirect() cover this? Or do I need to “go earlier” than that?

1 Like

You need to see if the query is done already. I think it is. But this hook is before the template file is determined, so how much earlier do you need?

2 Likes

So basically I have a number of roles set up.
But within that role, there is also different experience levels.

So to use a somewhat similar analogy that may be more familiar to everyone in the forums, let’s say new / basic / leader / committee.
And these users can have something similar to reputation levels.

I am checking that the user has the necessary role AND the necessary reputation to access the page.
If they don’t, I don’t want anything on the page to show up in their network traffic.
So I want the check to happen before anything, including the header on the page, is loaded.

I am not sure what exactly the hook sequence looks like, so I am not sure what is the best hook to use.

EDIT: I am not sure that I am using exactly the right terminology, so to make it as clear as I can, I want a redirect to happen before the page’s custom PHP (added via a reputable plugin from repo), or any of the custom JavaScript (added by another reputable plugin from repo) or any of the page’s HTML is loaded.

Here is front end actions sequence
(list from kama.ru with my comments):

  1. mu_plugin_loaded
  2. network_plugin_loaded
  3. muplugins_loaded
  4. registered_taxonomy
  5. registered_post_type
  6. plugin_loaded
  7. plugins_loaded
  8. sanitize_comment_cookies
  9. setup_theme
  10. load_textdomain
  11. after_setup_theme // The earliest for a theme; user is not authorised, pugins are not initialized
  12. auth_cookie_malformed
  13. auth_cookie_valid
  14. set_current_user // Triggered by kses, user is set
  15. init // The most common and popular hook for plugins. Here you can get and process request vars if needed. All globals are set, as well as user & taxonomies, but no headers sent still to client
  16. widgets_init
  17. register_sidebar
  18. wp_register_sidebar_widget
  19. wp_default_scripts // Provides access to WP_Scripts object
  20. wp_default_styles
  21. admin_bar_init
  22. add_admin_bar_menus
  23. wp_loaded // Almost the same state as init, check docs for differences
  24. parse_request
  25. send_headers // That’s just where output starts technically
  26. parse_query // Main query object is ready, you can check its parmeters
  27. pre_get_posts
  28. posts_clauses
  29. posts_selection
  30. wp // The most suitable place to edit main query before it executes
  31. template_redirect // The most suitable place to redirect. No content displayed still
  32. get_header // before displaying header.php
  33. wp_head // insecure third-party output starts just here
  34. wp_enqueue_scripts
  35. wp_print_styles
  36. wp_print_scripts

But depending on your task, you might not use hooks at all. If you are writing a page template and just want to hide some content for some users or roles, you can just do manual checks right before calling get_header. Something like this:

<?php /* Template Name: My Special Template */

global $wp, $wp_query, $wpdb; // if you need those

// Filtering anonymous users

if ( ! is_user_logged_in() ) {
	header("HTTP/1.0 403 Forbidden");
	exit;
}

// Filtering access by whitelist of IDs

$allowed_user_ids = array( 1, 2, 3 );
$current_user = wp_get_current_user();
if ( ! in_array ( $current_user->ID, $allowed_user_ids ) ) {
	header("HTTP/1.0 403 Forbidden");
	exit;
}

// Filtering access by user capability (role-based strategy)
$required_capability = 'edit_others_post';
if ( ! current_user_can( $required_capability ) ) {
	header("HTTP/1.0 403 Forbidden");
	exit;
};

// ... Enque your scripts and styles

// Call get_header() for standard design. Or just wp_head().
get_header();
1 Like

Thanks @norske !

This is incredibly informative.

So, my current check is something like this. I shortened it a bit and renamed for clarity.

$current_user = wp_get_current_user();
$current_user_id = wp_get_current_user_id();
$current_user_role = $current_user->role[0];
$allowed_roles = array (“basic”, “leader”, “committee”);
$current_user_reputation = get_the_user_meta($current_user_id, ‘reputation’, true);
$allowed_reputation = array (“medium”, “high”);
$url_of_a_page = “http://…/read-the-rules-first.com”;

// This is the part that I want to move to some point before wp_head();
// It is currently on the page itself
if ( $current_user_role === “new” ) { wp_safe_redirect ( $url_of_a_page ); exit; }

// This is the part that I want to stay on the page like you have in the custom template example
if ( in_array ($current_user_role, $allowed_roles) && in_array ($current_user_reputation, $allowed_reputation ) { // show this content }
elseif (in_array ($current_user_role, $allowed_roles) ) { // show that content }
elseif (in_array ($current_user_reputation, $allowed_reputation) ) { // show something different }
else { // catch all other scenarios content }

So if I understand your description above correctly, I need to choose a hook for the redirect that is after number 14, because if I understand correctly, I won’t be able to use wp_get_current_user(); before then, since the user is not set, and before number 32, since no content is displayed at 31 yet.

I genuinely appreciate the help a lot :bouquet:

Well, I’m not an expert in this, but here are some thoughts.

As wp_safe_redirect() is a pluggable function you can’t call it before all plugins are loaded anyway. So the absolute minimum is ‘plugins_loaded’. But as you also need access to current user, you have to wait al least till ‘init’ fires (all global and functions loaded).

Next step depends on what kind of content you plan to show. I’m not sure about how wp_redirect works in detail, so you’d better check the code. But to my mind If the target url is external it’s safer to to hook before ‘send_headers’. Just to be sure you won’t get PHP warning ‘Headers already sent’. Perhups ‘init’ or ‘wp_loaded’ as they are most common. But once again, it’s not an expert advice. Maybe it’s no trouble to hook later and I play safe too much.

If you want an internal redirect, it might be wiser to use template inclusion or template redirection instead, as @joyously mentioned above.

And of course you can use any conditional logic inside a loaded template to show different content to different types of user. As you control calling wp_head() or get_header() you can check required user meta before them without any additional hooks.

2 Likes

I’m still reading to try to improve my understanding on this :blush:

I read in the codex that $page (int) is a global variable.

Does this mean that is_page(); should be accessible from init(); onwards?
Is it also one of the pluggable functions?

If you are going to use wp_safe_redirect, then I would definitely recommend using the template_redirect hook. I do this a lot. Just remember to exit after the redirect.

1 Like

is_page() is just a shortcut for $wp_query->is_page(). So yes, function is available since global $wp_query is set. There is a proper check, so otherwise it returns false anyway.

$page is listed in “Inside the Loop variables”. In general you shouldn’t use it outside the Loop, because it can be overriden by secondary loops/queries in some cases.

Might also help:

  • get_queried_object()
  • get_queried_object_id()

The key concept here is WP_Query. All other stuff is just shortcuts/wrappers/getters for its methods and variables. Once you get global $wp_query, you get access to all needed values.

1 Like

+1 for template_redirect for this use case.

https://codex.wordpress.org/Plugin_API/Action_Reference/template_redirect:

This action hook executes just before WordPress determines which template page to load. It is a good hook to use if you need to do a redirect with full knowledge of the content that has been queried.

Also, in the future please enclose multi-line code blocks in three backquotes so that they are rendered correctly:

```
code goes here
```
1 Like

Thanks, @james

This sums up exactly where my issue is.
I’ve read through the entire Action Reference in the Codex and more details on the individual action hooks in the Codex, but I didn’t feel that I understand the nuance before @norske answered with solution marked as correct.
It really helps to see it in the order it happens with the comments.
I feel I am starting to understand, but not there yet. Still reading more.

My concern is exactly that I don’t want the redirect to happen “with full knowledge of the content being queried”.
I want the redirect to happen at the earliest possible opportunity when it is known that a user is requesting to view those specific pages. I don’t want the (full?) query to happen in the first place, if that makes sense.

1 Like

In order to know (correctly according to the WP loading cycle) which page a user is requesting, then you need to let the full query happen. Functions that are based on WP_Query like is_page() are only available after the query executes during the wp action.

You might be able to get away with checking the URL during init, but I would expect this to cause problems with some plugins. I still think template_redirect is the best choice for this task.

3 Likes

I wish I could upvote this more than once :rofl:

This is really one of THE most useful posts I have seen.
I keep referring back to it.

Would you mind for it to be turned into a tutorial, @norske?
It may take a while, but once it is done, it should be posted under your name.

3 Likes

Thanks, @anon71742606! Happy to know that it was helpful.

But all credits go to kama.ru, I just quoted a list from the original page:
https://wp-kama.ru/hooks/actions-order
By the way, it also contains a list of admin-side hooks, too.
(And there is even a complete database of WP hooks with useful filter)

I have no personal relation to that resourse. It’s a WP reference guide/handbook created by a passionte person - Timur. Probably this site is the oldest and the most popular wp-focused resourse in Runet. Many pages are in my bookmarks. The licence disallows direct copying of articles in whole, but allows quotations with opened hyperlinks, so I’m pretty sure you can use those lists in tutorial. But if you need some kind of guarantee, I can try to contact author and ask for a direct permission.

5 Likes

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.