Discuss your project

WordPress Request Lifecycle & Hooks

/* by - February 17, 2026 */
WordPress Request Lifecycle & Hooks

Introduction

Understanding WordPress hooks and request lifecycle is one of the most valuable skills a WordPress developer can have. Every time a user visits a WordPress page, a detailed sequence of events fires behind the scenes — and WordPress hooks let you tap into that sequence at precisely the right moment.

This post is your reference map. Whether you’re building a plugin, customizing a theme, or debugging a tricky redirect issue — knowing this lifecycle will save you hours.


What Are WordPress Hooks?

WordPress hooks are designated points in the request lifecycle where you can attach and execute your own code. They come in two types:

  • Actions (do_action) — WordPress announces “this just happened.” You listen and run your own logic at that exact moment using add_action().
  • Filters (apply_filters) — WordPress announces “I’m about to use this value.” You intercept it, modify it, and return it using add_filter().
// Action example — run your code at a specific moment
add_action('init', function() {
    register_post_type('vehicle', [...]);
});

// Filter example — modify a value before WordPress uses it
add_filter('the_content', function($content) {
    return $content . '<p>Thanks for reading!</p>';
});

Think of hooks like electrical outlets built into the walls of a house. The house works perfectly without you plugging anything in — but those outlets are there specifically so you can extend and customize without tearing the walls apart.


The WordPress Request Lifecycle

Below is the full flow of a WordPress request — from the moment a URL is hit to the moment a template renders. Every 🪝 hookable point is a place where WordPress hooks let you inject your own logic.

/index.php
    ↓
/wp-blog-header.php
    ↓
    wp_load() → /wp-load.php
                    ↓
                /wp-config.php
                    ↓
                /wp-settings.php
                    ├── All plugins loaded → do_action('plugins_loaded')
                    ├── Widgets init      → do_action('widgets_init')
                    └── do_action('init') ← register post types, taxonomies, rewrite rules
    ↓
    wp() → WP::main()
        ↓
        WP::parse_request()
            ├── Matches URL against rewrite rules
            ├── Sets query vars
            └── do_action('parse_request')     ← 🪝 hookable

        ↓
        WP::query_posts() → new WP_Query()
            └── do_action('parse_query')        ← 🪝 hookable

        ↓
        WP::handle_404()

        ↓
        WP::send_headers()
            └── do_action('send_headers')       ← 🪝 hookable

        ↓
        do_action('wp')                         ← 🪝 hookable (full WP context ready)

    ↓
    /wp-includes/template-loader.php
        ↓
        do_action('template_redirect')          ← 🪝 hookable (last chance before template)
        ↓
        apply_filters('template_include')       ← 🪝 hookable (override template file)
        ↓
        /wp-content/themes/your-theme/index.php
            ↓
            get_header()  → do_action('get_header')          ← 🪝 hookable
            get_footer()  → do_action('get_footer')          ← 🪝 hookable
            get_sidebar() → do_action('get_sidebar')         ← 🪝 hookable
            the_content() → apply_filters('the_content')     ← 🪝 hookable

WordPress Request Lifecycle Hooks — When to Use Each

Understanding the WordPress request lifecycle means knowing which hook serves which purpose. Here’s a quick reference:

HookTypeBest Used For
plugins_loadedActionLoad dependencies, check if other plugins exist
initActionRegister post types, taxonomies, rewrite rules
query_varsFilterAllowlist custom URL query parameters
parse_requestActionIntercept URL early, before WP_Query builds
parse_queryActionModify WP_Query object before DB query runs
send_headersActionAdd custom HTTP response headers
wpActionLogic that needs full WP context (post type, archive, etc.)
template_redirectActionRedirects, 404s, access control, before template loads
template_includeFilterOverride which template file WordPress loads
the_contentFilterModify post content before it renders

Quick Rule of Thumb

Need to act on the URL? → parse_request
Need to modify the query? → parse_query
Need WP context (is_single etc)? → wp
Need to redirect? → template_redirect
Need to change the template? → template_include
Need to tweak rendered content? → the_content

One Golden Rule

The earlier you hook, the less context you have. The later you hook, the less control you have.

Hook too early (like plugins_loaded) and WordPress hasn’t built the query yet — you won’t know if you’re on a single post or an archive. Hook too late (like the_content) and headers are already sent, redirects are impossible, and the template is already decided.

The sweet spot depends entirely on what your code needs to know and what it needs to control. Use the lifecycle map above to find that sweet spot every time.


Conclusion

WordPress’s hook system is what makes it infinitely extensible without ever touching core files. But like any powerful tool, it rewards those who understand it deeply.

Next time you’re about to write add_action() or add_filter(), pause for a second and ask — where in the lifecycle does this actually need to run? The answer to that question is often the difference between a clean solution and a frustrating debugging session three months later.

Bookmark this page. You’ll be back.

To learn more about how hooks are registered internally, refer to the official WordPress Plugin Handbook and the WordPress action reference on developer.wordpress.org.