The redirect screen in WebberZone Link Warnings supports theme template overrides and CSS customization.

Clicking a processed link opens an interstitial page when the warning method is Redirect screen or Inline indicators + Redirect screen, or when a Link Rule applies a redirect action. This page shows the destination URL and a message. It shows a countdown only when automatic redirection is allowed and Redirect Countdown is greater than zero.

How the redirect screen works

  1. The plugin registers a rewrite rule that maps external-redirect/ to a custom query variable.
  2. When a user clicks a processed link, the frontend JavaScript navigates to the signed external-redirect/ URL. Server-processed external content also receives signed URLs. The browser requests signatures through AJAX for links that PHP did not process or whose signatures need regeneration.
  3. On template_redirect, the plugin validates the signed destination URL and renders the redirect template.
  4. A separate JavaScript file (redirect.js) starts a countdown timer when the screen is allowed to redirect automatically. When the countdown reaches zero, the browser redirects to the destination.
  5. If the user clicks anywhere on the page (except the “Continue to site” button) or presses any key (except Tab), the automatic countdown is canceled.

The countdown only runs when Redirect_Handler::allows_countdown() approves it. A confirm-only signature never counts down. A full signature for a same-site destination is eligible for a countdown when Redirect Countdown is greater than zero and the template retains the countdown element. For an external destination, the screen counts down only for visitors who arrive from a page on this site, as reported by the Sec-Fetch-Site header (same-origin) or, in browsers without it, the Referer header. Opened from an email, a bookmark, or another site, the screen waits for the visitor to click Continue.

Redirect URL structure

The redirect URL is generated by Redirect_Handler::get_redirect_url():

https://yoursite.com/external-redirect/?url=<encoded-url>&wzlw_sig=<signature>

Confirm-only URLs add a third parameter:

https://yoursite.com/external-redirect/?url=<encoded-url>&wzlw_sig=<signature>&wzlw_confirm=1

The destination URL is passed as a url query parameter, encoded with rawurlencode(). Query parameters, fragments, and plus signs in the destination remain part of the signed value.

Since 1.7.0, wzlw_sig is an HMAC-SHA256 over a prefixed input, so the two signature kinds can never be swapped:

// Full signature — the screen may count down.
$signature = hash_hmac( 'sha256', 'full|v2|' . $destination, wp_salt( 'auth' ) );

// Confirm-only signature — the screen always waits for the click.
$signature = hash_hmac( 'sha256', 'confirm|' . $destination, wp_salt( 'auth' ) );

The destination is passed through esc_url_raw() before signing. Signatures produced by versions before 1.7.0 (a bare HMAC over the destination) are still accepted, but only as confirm-only: the screen always waits for the click.

Open redirect protection

Redirect_Handler::is_valid_url() accepts a destination in any of these forms:

  • Root-relative paths with no host and no scheme (e.g. /go/product/) (treated as same-site and valid on their own. This covers cloaked affiliate redirect links marked with the Affiliate Link Class or Affiliate Link Wrapper Class, which point at internal paths. A scheme-bearing but host-less value (e.g. http:evil.com) is deliberately rejected here, since browsers resolve that against a remote host when the scheme differs from the page’s) an authority-less path is only trusted when the scheme is absent too.
  • Network-path references such as //example.com/page, which have a host but no scheme.
  • Absolute http and https URLs that pass filter_var() with FILTER_VALIDATE_URL. Same-site absolute URLs are valid because force-external and internal target="_blank" links can also use the redirect screen.

The HMAC signature authorizes the destination. The host does not need to differ from the site host. Values using other schemes, such as mailto:, or values that fail validation send the user to the site home page via wp_safe_redirect().

Each signature kind has its own HMAC input (full|v2| or confirm|), so a confirm-only signature can never be turned into an auto-redirecting one. Server processing of comment text, previews, and links carrying rel="ugc" requests confirm-only signatures unless the destination is same-site or matches a redirect rule. The browser also recognizes previews and rel="ugc", but cannot identify browser-only comment links without that attribute. Re-signing preserves an existing confirm-only request. The AJAX endpoint accepts confirm=1 for these requests; it does not independently determine where the source link appeared.

A ready-made fully signed redirect URL pasted into a comment is already a same-site link and is not reclassified by its original source. The external destination still requires a same-origin browser arrival before it can count down.

Template override

The default template is located at:

includes/templates/redirect-screen.php

To override it, copy the file to your theme (or child theme):

your-theme/webberzone-link-warnings/redirect-screen.php

The plugin checks for the template in the following order:

  1. your-theme/webberzone-link-warnings/redirect-screen.php
  2. your-theme/webberzone-link-warnings/redirect.php
  3. The plugin’s built-in template.

This uses WordPress’s locate_template(), so child themes take priority over parent themes.

Available template variables

The following variables are available inside the redirect template:

VariableTypeDescription
$destinationstringThe signed destination URL, which can also be a same-site or root-relative URL.
$messagestringThe matching redirect rule’s nonempty message, or the configured Redirect Message.
$domainstringThe host portion of the destination URL (e.g. example.com).
$countdownintThe countdown duration in seconds. 0 means auto-redirect is disabled.

Writing a custom template

A minimal custom template:

<?php
/**
 * Custom redirect screen template.
 *
 * @var string $destination Destination URL.
 * @var string $message     Redirect message.
 * @var string $domain      Destination domain.
 * @var int    $countdown   Countdown in seconds.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

get_header();
?>
<div class="my-redirect-page">
    <h1>Leaving this site</h1>
    <p><?php echo esc_html( $message ); ?></p>
    <p>Destination: <strong><?php echo esc_html( $domain ); ?></strong></p>
    <p><a href="<?php echo esc_url( $destination ); ?>" rel="noopener noreferrer">Continue to site</a></p>
    <p><a href="<?php echo esc_url( wp_get_referer() ? wp_get_referer() : home_url() ); ?>">Go back</a></p>

    <?php if ( $countdown > 0 ) : ?>
        <p class="wzlw-redirect-countdown" aria-live="polite">
            Redirecting automatically in <span class="wzlw-countdown-number"><?php echo esc_html( $countdown ); ?></span> seconds...
        </p>
    <?php endif; ?>
</div>
<?php
get_footer();

The countdown JavaScript targets .wzlw-countdown-number to update the displayed number. If you remove or rename this class, redirect.js returns before starting the timer. Automatic redirection stops as well.

Countdown behavior

The countdown is handled by redirect.js, which is enqueued automatically on the redirect page. It reads its configuration from a localized JavaScript object:

wzlwRedirect.destination // The destination URL.
wzlwRedirect.countdown   // The countdown duration in seconds.

When the screen must wait for a click (a confirm-only signature, or a visitor who did not arrive from this site), the server localizes an empty destination and a countdown of 0, so redirect.js never starts a timer.

Disabling auto-redirect

Set Redirect Countdown to 0 in the plugin settings. The countdown element will not be rendered, and the JavaScript will not start a timer. The user must click “Continue to site” manually.

Canceling the countdown

The countdown is canceled automatically if the user:

  • Clicks anywhere on the page (except the “Continue to site” link).
  • Presses any key except Tab.

This prevents the redirect from firing while the user is interacting with the page.

Redirect screen assets

The plugin enqueues the following assets on the redirect page only:

HandleFilePurpose
wzlw-redirect (CSS)includes/assets/css/redirect.cssPage layout, card, buttons, countdown animation.
wzlw-redirect (JS)includes/assets/js/redirect.jsCountdown timer and auto-redirect logic.

Both assets respect SCRIPT_DEBUG (loading unminified versions when enabled) and is_rtl() (loading RTL stylesheets when appropriate).

If you are using a fully custom template and do not need the default styles, you can dequeue them:

add_action( 'wp_enqueue_scripts', function () {
    if ( get_query_var( 'wzlw_redirect' ) ) {
        wp_dequeue_style( 'wzlw-redirect' );
    }
}, 20 );

Keep the JavaScript enqueued to ensure the countdown functions. If you are implementing your own countdown logic, you can dequeue the script as well.

Styling the default template with CSS

If you do not need a full template override, you can restyle the default redirect screen using CSS custom properties. See the Styling guide for the full list of redirect-related custom properties and class names.

Was this article helpful?