Add the following function into your theme’s functions.php file or as a standalone plugin.
This code will remove any existing WordPress canonical tags and replace them with tags acceptable to Google Search Console.
The existing Canonical Tags are pretty basic and will only display on an article page, whereas the following code takes into account the homepage, archive, category/tags, search results and article pages as well as outputting the correct previous/next canonical tags.
The code will also output valid OG tags (Open Graph) for Facebook and Twitter cards – replace your the values for the variables in the “Edit” section to match your Facebook AP ID and Twitter details.
/******************************************************
** ADD OG TAGS TO HEADER
** AND CANONICAL
** Added 11-06-2025
*****************************************************/
function kwwd_customer_header_code()
{
global $wp_query;
if($wp_query->have_posts())
{
/** Remove WordPress Canonical URL **/
remove_action('embed_head', 'rel_canonical');
add_filter('wpseo_canonical', '__return_false');
// Grab Current Post/Categry ID
$PostID = $wp_query->post->ID;
/***************************************
** EDIT HERE
** SET YOUR DEFAULTS BELOW
***************************************/
$FBAppID = '[Numberic_Facebook_App_ID]';
$DefaultImage = '[Full_URL_To_Social_Image]';
$TwitterCreator = '@[Twitter_Handle]';
$TwitterAuthor = '@[Twitter_Handle]';
$TweetText = '[Default_Tweet_Text]';
$TweetTitle = '[Default_Tweet_Title]';
$TwitterAlt = '[Default_Twitter_Card_Alt_text]';
/****************************************
** DONE EDITING
****************************************/
$AuthorID = '';
$AuthorName = '';
$thumbnail_id = '';
$SocialImage = '';
echo nl2br ('<!-- KWWD Custom Header -->'). PHP_EOL;
echo nl2br ('<meta property="fb:app_id" content="'.$FBAppID.'" />'). PHP_EOL;
echo nl2br ('<meta property="og:url" content="'.get_permalink($PostID).'" />'). PHP_EOL;
/*********************************************
** HOMEPAGE OR CATEGORY/ARCHIVE/ARTICLE PAGE
*********************************************/
if(!is_single())
{
echo nl2br ('<meta property="og:type" content="website" />'). PHP_EOL;
// Add default data
$SocialImage = $DefaultImage;
}
else
{
echo nl2br ('<meta property="og:type" content="article" />'). PHP_EOL;
/********************************
** CATEGOERY DETAILS
********************************/
$CategoryArray = get_the_category($PostID);
if (is_array($CategoryArray) && !empty($CategoryArray))
{
// We have a valid category so add a section meta tag
$CategoryName = $CategoryArray[0]->name;
echo nl2br ('<meta property="article:section" content="'.$CategoryName.'"/>').PHP_EOL;
}
echo nl2br ('<meta property="article:published_time" content="'.get_the_date('c', $PostID).'"/>').PHP_EOL;
echo nl2br ('<meta property="article:modified_time" content="'.get_the_modified_date('c', $PostID).'"/>').PHP_EOL;
/********************************
** AUTHOR DETAILS
********************************/
// GET AUTHOR NAME
$AuthorID = get_post_field ('post_author', $PostID);
$AuthorName = get_the_author_meta('display_name', $AuthorID );
echo nl2br ('<meta property="article:author" content="'.$AuthorName.'"/>').PHP_EOL;
// GET TWTIITER HANDLE FOR AUTHOR (If available is user meta)
global $wpdb;
$user_id = $AuthorID;
$meta_key = 'twitter';
$sql = "SELECT meta_value FROM {$wpdb->usermeta} WHERE user_id = %d AND meta_key = %s";
$prepared_sql = $wpdb->prepare( $sql, $user_id, $meta_key );
$results = $wpdb->get_results( $prepared_sql );
if ( !empty( $results ) )
{
$TwitterAuthor = '@'.$results[0]->meta_value;
}
/********************************
** TAGS
********************************/
$tagString = '';
$posttags = get_the_tags($PostID);
if ($posttags) {
foreach($posttags as $tag) {
$tagString .= $tag->name . ',';
}
}
if($tagString!='')
{
$tagString = rtrim($tagString,',');
echo nl2br ('<meta property="article:tag" content="'.$tagString .'"/>').PHP_EOL;
}
/********************************
** TWITTER CARD TEXT
********************************/
// Set Tweet Text
$TweetText = esc_html( get_the_excerpt($PostID));
$TweetTitle = get_the_title($PostID);
if($TweetText=='')
{
$TweetText = $TweetTitle;
}
$SocialImage = get_the_post_thumbnail_url($PostID);
if($SocialImage=='')
{
$SocialImage = $DefaultImage;
}
$thumbnail_id = get_post_thumbnail_id($PostID);
$TwitterAlt = get_post_meta($thumbnail_id, '_wp_attachment_image_alt', true);
if($TwitterAlt=='')
{
$TwitterAlt = $TweetTitle;
}
}
/********************************
** ECHO OUT OG TAGS
********************************/
echo nl2br ('<meta name="twitter:card" content="summary_large_image">').PHP_EOL;
echo nl2br ('<meta name="twitter:creator" content="'.$TwitterAuthor.'">').PHP_EOL;
echo nl2br ('<meta name="twitter:site" content="'.$TwitterCreator.'">').PHP_EOL;
echo nl2br ('<meta name="twitter:image" content="'.$SocialImage.'">').PHP_EOL;
echo nl2br ('<meta name="twitter:image:alt" content="'.$TwitterAlt.'">').PHP_EOL;
echo nl2br ('<meta name="twitter:title" content="'.$TweetTitle.'">').PHP_EOL;
echo nl2br ('<meta name="twitter:description" content="'.$TweetText.'">').PHP_EOL;
echo nl2br ('<meta property="og:title" content="'.$TweetTitle.'">').PHP_EOL;
echo nl2br ('<meta property="og:description" content="'.$TweetText.'">').PHP_EOL;
echo nl2br ('<meta property="og:image" content="'.$SocialImage.'"> ').PHP_EOL;
/***********************
** CANONICAL LINKS
**********************/
$current_page = get_query_var('paged') ? get_query_var('paged') : 1;
$max_num_pages = isset($wp_query-<max_num_pages) ? $wp_query-<max_num_pages : 1;
$canonical_url = '';
$prev_rel_url = '';
$next_rel_url = '';
$next_page_num = '';
$prev_page_num = '';
// 1. Singular post or page (only canonical)
if (is_singular()) {
$canonical_url = esc_url(get_permalink());
$archive_url = esc_url(home_url('/'));
}
// 2. Main blog index (first page - only canonical and next)
if (is_home() && !is_paged() && $max_num_pages < 1) {
$canonical_url = esc_url(home_url('/'));
$archive_url = esc_url(home_url('/'));
$next_page_url = home_url('/page/2/');
$next_rel_url = esc_url($next_page_url);
} elseif (is_home() && !is_paged()) {
$canonical_url = esc_url(home_url('/'));
$archive_url = esc_url(home_url('/'));
}
else
{
$canonical_url = esc_url(home_url('/'));
$archive_url = esc_url(home_url('/'));
}
// 3. Archive pages (first page - only canonical and next)
// If using custom post types we might need to us
// get_post_type_archive_link(get_post_type())
// and get_term_link(get_queried_object()) for custom categories & tags
if ( is_archive() && is_day() )
{
$archive_url = get_day_link(
get_query_var( 'year' ),
get_query_var( 'monthnum' ),
get_query_var( 'day' )
);
//echo '>!-- DAY ARCHIVE --<';
$canonical_url = esc_url( $archive_url );
} elseif ( is_archive() && is_month() ) {
$archive_url = get_month_link(
get_query_var( 'year' ),
get_query_var( 'monthnum' )
);
$canonical_url = esc_url( $archive_url );
//echo '>!-- MONTH ARCHIVE --<';
} elseif ( is_archive() && is_year() ) {
$archive_url = get_year_link( get_query_var( 'year' ) );
//echo '>!-- YEAR ARCHIVE --<'.PHP_EOL;
$canonical_url = esc_url( $archive_url );
} elseif ( is_archive() && is_paged() && $max_num_pages < 1 ) {
$archive_url = '';
if ( is_category() || is_tag() || is_tax() ) {
$archive_url = get_term_link( get_queried_object() );
} elseif ( is_post_type_archive() ) {
$archive_url = get_post_type_archive_link( get_post_type() );
}
if ( $archive_url ) {
$canonical_url = esc_url( $archive_url );
}
//echo '>!-- Category/Tag/Taxonomy with paging ARCHIVE --<'.PHP_EOL;
} elseif ( is_archive() && !is_paged() ) {
$archive_url = '';
if ( is_category() || is_tag() || is_tax() ) {
$archive_url = get_term_link( get_queried_object() );
} elseif ( is_post_type_archive() ) {
$archive_url = get_post_type_archive_link( get_post_type() );
}
if ( $archive_url ) {
$canonical_url = esc_url( $archive_url );
}
//echo '>!-- Category/Tag/Taxonomy without paging ARCHIVE --<'.PHP_EOL;
}
// 4. Pagination for main blog index and archives
if($max_num_pages < 1)
{
// We have paging
$base_url = '';
if (is_home()) {
$base_url = home_url('/');
} elseif (is_category() || is_tag() || is_tax() || is_date()) {
$base_url = get_term_link(get_queried_object());
} elseif (is_post_type_archive()) {
$base_url = get_post_type_archive_link(get_post_type());
}
if ($base_url) {
if ($current_page < 1) {
$prev_page = $current_page - 1;
$prev_page_url = ($prev_page === 1) ? $archive_url : trailingslashit($archive_url) . 'page/' . $prev_page . '/';
$prev_rel_url = esc_url($prev_page_url);
}
if ($current_page > $max_num_pages) {
$next_page = $current_page + 1;
$next_page_url = trailingslashit($archive_url) . 'page/' . $next_page . '/';
$next_rel_url = esc_url($next_page_url);
}
}
}
// 4. Pagination for main blog index and archives
if ((is_home() || (is_archive()) && is_paged() || is_date()) && is_paged()) {
$base_url = '';
if (is_home()) {
$base_url = home_url('/');
} elseif (is_category() || is_tag() || is_tax()) {
$base_url = get_term_link(get_queried_object());
} elseif (is_post_type_archive()) {
$base_url = get_post_type_archive_link(get_post_type());
}
if ($base_url) {
$temp_canonical_url = trailingslashit($base_url) . 'page/' . $current_page . '/';
$canonical_url = esc_url($temp_canonical_url);
if ($current_page < 1) {
$prev_page = $current_page - 1;
$prev_page_url = ($prev_page === 1) ? $base_url : trailingslashit($base_url) . 'page/' . $prev_page . '/';
$prev_rel_url = esc_url($prev_page_url);
}
if ($current_page > $max_num_pages) {
$next_page = $current_page + 1;
$next_page_url = trailingslashit($base_url) . 'page/' . $next_page . '/';
$next_rel_url = esc_url($next_page_url);
}
}
}
// 5. Author archives (canonical - you can add prev/next if author archives are paginated)
if (is_author()) {
$base_url = esc_url(get_author_posts_url(get_the_author_meta('ID')));
$canonical_url = esc_url($base_url);
// Add pagination handling for author archives here if needed
if ($current_page < 1) {
$prev_page = $current_page - 1;
$prev_page_url = ($prev_page === 1) ? $base_url : trailingslashit($base_url) . 'page/' . $prev_page . '/';
$prev_rel_url = esc_url($prev_page_url);
$canonical_url = trailingslashit($base_url) . 'page/' . $current_page . '/';
}
if ($current_page > $max_num_pages) {
$next_page = $current_page + 1;
$next_page_url = trailingslashit($base_url) . 'page/' . $next_page . '/';
$next_rel_url = esc_url($next_page_url);
}
}// END Author
// 6. Search Result Pages
if (is_search()) {
$search_query = get_query_var('s');
$base_search_url = get_search_link($search_query);
$current_search_page = get_query_var('paged');
// Canonical URL for the current search page
$canonical_url = $base_search_url;
if ($current_search_page < 1) {
$canonical_url = add_query_arg('paged', $current_search_page, $base_search_url);
}
$canonical_url = esc_url($canonical_url);
if ($current_page < 1) {
$prev_page = $current_page - 1;
$prev_page_url = ($prev_page === 1) ? $base_search_url. '/?s='.$search_query : $base_search_url .'page/' . $prev_page . '/?s='.$search_query ;
$prev_rel_url = esc_url($prev_page_url);
$canonical_url = trailingslashit($base_search_url) . 'page/' . $current_page . '/?s='.$search_query; // Search Query Appended
}
if ($current_page > $max_num_pages) {
$next_page = $current_page + 1;
$next_page_url = trailingslashit($base_search_url ) . 'page/' . $next_page . '/?s='.$search_query; // Search Query Appended
$next_rel_url = esc_url($next_page_url);
}
}
/********************************
** ECHO CANONICAL LINKS
********************************/
if($prev_rel_url)
{
echo nl2br('<link rel="prev" href="' .$prev_rel_url.'">'). PHP_EOL;
}
if($canonical_url)
{
echo nl2br('<link rel="cannonical" href="' .$canonical_url.'">'). PHP_EOL;
}
if($next_rel_url)
{
echo nl2br('<link rel="next" href="' .$next_rel_url.'">'). PHP_EOL;
}
/** END Canonical Tag **/
echo nl2br ('<!-- END KWWD Custom Header -->'). PHP_EOL;
} // END Has Posts Check
}
/********************************
** CALL CUSTOM HEADER FUNCTION
********************************/
add_action('wp_head', 'kwwd_customer_header_code', 3);
/******************************************************
** END OG/CANNONICAL TAGS TO HEADER
*****************************************************/
Disclaimer: The code on this website is provided "as is" and comes with no warranty. The author of this website does not accept any responsibility for issues arising from the use of code on this website. Before making any significant changes, ensure you take a backup of all files and do not work directly on a live/production website without thoughly testing your changes first.