Find Broken Internal Links (WP-CLI)
Adds a "wp check-internal-links" command that scans published content for links to your own site that no longer point to a post, page or file, and lists them with the post they appear in.
Opens WPIDE on
Code
if ( ! defined( 'WP_CLI' ) || ! WP_CLI ) {
return;
}
WP_CLI::add_command( 'check-internal-links', function ( $args, $assoc_args ) {
$home = untrailingslashit( home_url() );
$post_types = isset( $assoc_args['post_type'] ) ? array_map( 'sanitize_key', explode( ',', $assoc_args['post_type'] ) ) : array( 'post', 'page' );
$items = array();
$paged = 1;
do {
$query = new WP_Query( array(
'post_type' => $post_types,
'post_status' => 'publish',
'posts_per_page' => 100,
'paged' => $paged++,
'no_found_rows' => true,
) );
foreach ( $query->posts as $post ) {
if ( ! preg_match_all( '/href=["\']([^"\'#]+)/i', $post->post_content, $matches ) ) {
continue;
}
foreach ( array_unique( $matches[1] ) as $url ) {
if ( 0 === strpos( $url, '/' ) && 0 !== strpos( $url, '//' ) ) {
$url = $home . $url;
}
if ( 0 !== strpos( $url, $home ) ) {
continue; // External link.
}
$path = (string) wp_parse_url( $url, PHP_URL_PATH );
if ( '' === trim( $path, '/' ) || false !== strpos( $path, '/wp-' ) ) {
continue; // Homepage, admin or uploads.
}
if ( url_to_postid( $url ) || file_exists( ABSPATH . ltrim( $path, '/' ) ) ) {
continue;
}
$items[] = array( 'post_id' => $post->ID, 'post_title' => $post->post_title, 'link' => $url );
}
}
} while ( count( $query->posts ) === 100 );
if ( ! $items ) {
WP_CLI::success( 'No broken internal links found.' );
return;
}
WP_CLI\Utils\format_items( $assoc_args['format'] ?? 'table', $items, array( 'post_id', 'post_title', 'link' ) );
}, array(
'shortdesc' => 'Lists internal links in content that do not resolve to anything.',
'synopsis' => array(
array( 'type' => 'assoc', 'name' => 'post_type', 'optional' => true, 'description' => 'Comma-separated post types. Default: post,page.' ),
array( 'type' => 'assoc', 'name' => 'format', 'optional' => true, 'default' => 'table', 'options' => array( 'table', 'csv', 'json', 'count' ) ),
),
) );
Notes
It checks links without making HTTP requests, so archive, search and plugin-made URLs may show up; review the list before editing.
How to add this snippet
- Install WPIDE on your WordPress site (it is free) and open Code Snippets.
- Use snippet above adds it from the library in one click. Or copy the code into a new snippet with the same code type.
- It is added inactive: review the code and settings, then switch it on.