WordPress has a built-in function called wp_safe_redirect()
. This allows you to create redirects in code, but only to whitelisted domains (via the allowed_redirect_hosts
filter).
The downside to this is that you have to remember to whitelist the domains. It’s easy to forget if you’re doing a lot of redirects, for instance with the WPCOM Legacy Redirector plugin.
When this happens, all un-whitelisted redirects will be redirected by default to /wp-admin/
instead, and can cause a headache trying to figure out what’s going wrong.
I had an idea to solve this problem. A simple logging plugin that logs failed redirects and adds a dashboard widget to show the domains and number of times the redirect has failed:
The code behind this:
<?php
class Emrikol_WSRD_Dashboard {
public static function instance() {
static $instance = false;
if ( ! $instance ) {
$instance = new Emrikol_WSRD_Dashboard();
}
return $instance;
}
public function __construct() {
add_action( 'init', array( $this, 'init' ) );
add_filter( 'allowed_redirect_hosts', array( $this, 'check_redirect' ), PHP_INT_MAX, 2 );
}
public function init() {
if ( $this->is_admin() && isset( $_GET['wsrd_delete'] ) && check_admin_referer( 'wsrd_delete' ) && isset( $_GET['ID'] ) ) {
$post_id = (int) $_GET['ID'];
if ( 'wsrd' !== get_post_type( $post_id ) ) {
// This isn't the right post type, abort!
add_action( 'admin_notices', array( $this, 'message_log_not_deleted' ) );
return;
}
$delete = wp_delete_post( $post_id, true );
wp_cache_delete( 'wsrd_report' );
if ( $delete ) {
add_action( 'admin_notices', array( $this, 'message_log_deleted' ) );
} else {
add_action( 'admin_notices', array( $this, 'message_log_not_deleted' ) );
}
}
$args = array(
'supports' => array( 'title' ),
'public' => false,
);
register_post_type( 'wsrd', $args );
add_action( 'wp_dashboard_setup', array( $this, 'add_dashboard_widgets' ) );
}
public function add_dashboard_widgets() {
if ( $this->is_admin() ) {
wp_add_dashboard_widget( 'emrikol_wsrd_dashboard', 'Failed Safe Redirects', array( $this, 'show_admin_dashboard' ) );
}
}
public function check_redirect( $allowed_hosts, $redirect_host ) {
if ( ! in_array( $redirect_host, $allowed_hosts, true ) ) {
// No redirect, please record.
$found_host = new WP_Query( array(
'fields' => 'ids',
'name' => md5( $redirect_host ),
'post_type' => 'wsrd',
'post_status' => 'any',
'no_found_rows' => true,
'posts_per_page' => 1,
'update_post_term_cache' => false,
'update_post_meta_cache' => false,
) );
if ( empty( $found_host->posts ) ) {
// No past redirect log found, create one.
$args = array(
'post_name' => md5( $redirect_host ),
'post_title' => $redirect_host,
'post_type' => 'wsrd',
'meta_input' => array(
'count' => 1,
),
);
$insert = wp_insert_post( $args );
} else {
// Found! Update count.
$count = absint( get_post_meta( $found_host->posts[0], 'count', true ) );
$count++;
update_post_meta( $found_host->posts[0], 'count', $count );
}
}
// We don't want to modify, always return allowed hosts unharmed.
return $allowed_hosts;
}
public function show_admin_dashboard() {
global $wpdb;
$report = wp_cache_get( 'wsrd_report' );
if ( false === $report ) {
$report = $wpdb->get_results( "SELECT ID, post_title AS host, meta_value AS count FROM $wpdb->posts LEFT JOIN $wpdb->postmeta ON ( $wpdb->posts.ID = $wpdb->postmeta.post_id ) WHERE post_type='wsrd' ORDER BY ABS( count ) DESC LIMIT 20;" );
wp_cache_set( 'wsrd_report', $report, 'default', MINUTE_IN_SECONDS * 5 );
}
?>
<style>
table#wsrd {
border-collapse: collapse;
width: 100%;
}
table#wsrd th {
background: #f5f5f5;
}
table#wsrd th, table#wsrd td {
border: 1px solid #f5f5f5;
padding: 8px;
}
table#wsrd tr:nth-child(even) {
background: #fafafa;
}
</style>
<div class="activity-block">
<?php if ( empty( $report ) ) : ?>
<p><strong>None Found!</strong></p>
<?php else : ?>
<table id="wsrd">
<thead>
<tr>
<th>Domain</th>
<th>Count</th>
<th>Control</th>
</tr>
</thead>
<tbody>
<?php foreach ( $report as $line ) : ?>
<tr>
<td><?php echo esc_html( $line->host ); ?></td>
<td><?php echo esc_html( $line->count ); ?></td>
<td><a href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'wsrd_delete' => true, 'ID' => rawurlencode( $line->ID ) ), admin_url() ), 'wsrd_delete' ) ); ?>">Delete</a></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<?php
}
public function message_log_deleted() {
echo '<div id="message" class="notice notice-success is-dismissible"><p>Redirect log deleted!</p></div>';
}
public function message_log_not_deleted() {
echo '<div id="message" class="notice notice-error is-dismissible"><p>Redirect log delete failed!</p></div>';
}
private function is_admin() {
if ( current_user_can( 'manage_options' ) ) {
return true;
}
return false;
}
}
Emrikol_WSRD_Dashboard::instance();
Code language: HTML, XML (xml)
Leave a Reply