How to fix Elementor Pro's fixed post-login redirect with a cookie-based routing system to protect multiple WordPress pages by user role — full blueprint included.
AI-Ready Blueprints, Tools

Role-Based Page Protection for the Elementor Pro Login Widget

How to fix Elementor Pro’s fixed post-login redirect with a cookie-based routing system to protect multiple WordPress pages by user role — full blueprint included.
role-based-page-protection-blueprint

What to do when "Redirect After Login" only accepts one fixed URL, and more than one page on the site needs restricting.

Elementor Pro's native login form looks like any other WordPress login widget until you try to make it behave like one. Under the hood it submits via AJAX, not a standard page POST, and that single architectural choice quietly breaks the pattern most developers reach for first when restricting a page to specific roles.

The instinct is to append a redirect_to parameter to the login URL, the way wp_login_form() or a classic wp-login.php flow would handle it. With the Elementor widget, that parameter goes nowhere. The form's Redirect After Login field in the widget settings takes exactly one static URL, set once, applied to everyone, regardless of which page sent them there.

The scenario that exposes the problem

Picture a page restricted to administrators and one other role — contributors, say, or any role that isn't the one usually associated with "protected content." A visitor without the right role hits the page, gets bounced to login, logs in successfully, and lands... on the homepage. Not on the page they wanted. Every time. The login worked. The experience didn't.

Multiply that by every protected page on the site and you get a login form that technically authenticates but functionally loses the visitor's original intent on every single visit.

Why the fix has to route through a cookie

Since the widget won't carry a dynamic redirect target through its AJAX submission, the destination has to survive somewhere else — a place the login process doesn't need to know about at all. A cookie does exactly that. Set it before the redirect to login, read it after the login completes, and the widget's fixed redirect URL becomes a waypoint rather than a destination.

Two hooks, both on template_redirect, do the whole job.

Step one: catching the unauthorized visit

This runs on every protected page. If the visitor isn't logged in, or is logged in but lacks the right role, it stores the current URL in a cookie and sends them to the login page.

add_action( 'template_redirect', function() {
    $protected_pages = array( 827, 4929 );

    if ( is_page( $protected_pages ) ) {
        if ( ! is_user_logged_in() || ! ( current_user_can( 'administrator' ) || current_user_can( 'contributor' ) ) ) {
            setcookie( 'site_redirect_to', get_permalink(), time() + 300, ( COOKIEPATH ?: '/' ), COOKIE_DOMAIN );
            wp_redirect( home_url( '/account/' ) );
            exit;
        }
    }
}, 5 );

is_page() accepts an array natively, so adding a new protected page later means adding one number, nothing else changes. The role check here uses specific role names rather than a shared capability — worth adjusting to whatever roles actually apply, since "administrator and contributor" is a fairly unusual pairing and mostly serves as an example.

The 300-second cookie expiry is worth a note. Five minutes — long enough to cover a fumbled password on a second attempt, short enough that the cookie doesn't linger as dead weight for a visitor who abandoned the flow entirely.

Step two: closing the loop after login

The widget's own redirect setting sends every successful login to a fixed page — the homepage, typically. This second hook intercepts right there, checks for the cookie, and — if the now-authenticated visitor actually has permission — sends them on to where they originally wanted to go.

add_action( 'template_redirect', function() {
    if ( is_front_page() && is_user_logged_in() && isset( $_COOKIE['site_redirect_to'] ) ) {
        if ( current_user_can( 'administrator' ) || current_user_can( 'contributor' ) ) {
            $redirect_to = esc_url_raw( $_COOKIE['site_redirect_to'] );
            setcookie( 'site_redirect_to', '', time() - 3600, ( COOKIEPATH ?: '/' ), COOKIE_DOMAIN );
            wp_redirect( $redirect_to );
            exit;
        }
    }
}, 5 );

Notice the role check happens twice — once before the redirect to login, once again after. That's not redundancy for its own sake. A visitor could theoretically log in as a role that doesn't qualify, and without the second check, the cookie would still fire and grant access it shouldn't. The cookie carries a destination, not a permission.

The cookie clears itself immediately after use, via a negative expiry timestamp. esc_url_raw() on the way out matters too — the cookie value is user-influenced data, even if only indirectly, and it's going straight into a redirect.

Configuring the widget itself

In the Elementor editor, the login widget's Redirect After Login field points to the homepage — nothing clever there, it's meant to be boring. All the actual routing intelligence lives in the two PHP hooks, not in the widget configuration.

Where this quietly breaks: caching

If the site runs full-page caching — hosting-level, a caching plugin, or Elementor's own Element Caching feature from version 3.22 onward — a protected page can get cached once while a logged-in, authorized visitor is viewing it, and that cached copy then gets served to everyone afterward regardless of role. The template_redirect hook never runs for those later visitors, because the cached HTML is served before WordPress processes anything.

Any page carrying this logic needs a permanent cache exclusion, set up at the same time the protection goes live — not something to remember to do later, because by the time it's noticed, it's usually because someone unauthorized already saw content they shouldn't have.

A lighter alternative worth ruling out first

If the actual need is showing or hiding a block of content on an otherwise public page — not blocking an entire page — this whole cookie/redirect system is more than what's required. A simple role-check shortcode handles that case without any of the above:

add_shortcode( 'restricted_content', function( $atts, $content = null ) {
    if ( ! is_user_logged_in() ) {
        return '';
    }
    $user = wp_get_current_user();
    if ( in_array( 'administrator', $user->roles ) || in_array( 'contributor', $user->roles ) ) {
        return do_shortcode( $content );
    }
    return '';
} );

I mention it because the two needs get confused often enough that it's worth ruling one out explicitly before building the more involved system.

Scope and requirements

This approach assumes a single fixed login destination and a small, known set of protected pages — a site with dozens of protected pages under varied role requirements would benefit from a settings-driven approach instead of hardcoded arrays, and that's a reasonable next step rather than something this system builds by default.

It requires Elementor Pro active, specifically the Login widget from the Form module, and assumes cookies are enabled in the visitor's browser — a cookie-blocked session degrades to "always lands on the homepage after login," an acceptable fallback rather than a hard failure.

Getting a working copy of this

The full technical specification is available as a blueprint document, meant to be handed to an AI coding assistant along with your own page IDs, roles, and login page setup. It works with Google AI Studio, Claude Projects, Qwen Chat, or any comparable tool capable of reading an attached file and generating PHP.

You can
Download the Blueprint (.md)

Need help with this solution or looking for custom development?

Visit my Stay In Touch page to connect and discuss your project. Discover a range of web development services and specialized WordPress solutions tailored to your needs. Let's work together to enhance your digital presence.