TechEarl

Remove the WordPress Shortlink (rel=shortlink) from the Head

How to remove the WordPress rel=shortlink: a small remove_action snippet that strips the <link rel='shortlink'> from the head and the matching Link: HTTP header.

Ishan Karunaratne⏱️ 7 min readUpdated
Share thisCopied
How to remove the WordPress shortlink: a remove_action snippet that strips the rel=shortlink tag from wp_head and the matching Link: rel=shortlink HTTP header from every page.

To remove the WordPress shortlink, drop this remove_action() block into your theme's functions.php (or, better, a small site-specific plugin). It stops the <link rel='shortlink'> tag printing in the <head> and removes the matching Link: HTTP header:

php
add_action( 'init', function () {
    remove_action( 'wp_head', 'wp_shortlink_wp_head', 10 );
    remove_action( 'template_redirect', 'wp_shortlink_header', 11, 0 );
} );

That is the whole fix. The first line clears the tag from the page source; the second clears the header, which is the part most snippets on the web forget. Both have to go or you have only half-removed it.

The WordPress shortlink is the ?p=123 style URL that WordPress advertises for every post and page, resolving to the same content as your pretty permalink. It surfaces in two places.

First, a tag in the <head> on singular views:

html
<link rel='shortlink' href='https://example.com/?p=123' />

Second, an HTTP response header on the same requests:

text
Link: <https://example.com/?p=123>; rel=shortlink

The feature dates back to WordPress 3.0 and was meant for sharing tools and services that wanted a compact URL. In practice almost nothing consumes it now: social platforms shorten links themselves, and you are far more likely to share the real permalink. So for most sites the shortlink is two pieces of output advertising a URL you would rather people did not use, because the ?p=123 form is a second, uglier address for a page that already has a clean canonical one.

How much speed does this actually save?

Almost nothing, and I want to be honest about that up front. This is a tidiness and canonicalisation change far more than a performance one.

The head tag is a few dozen bytes. The HTTP header is a few dozen more. You are not removing a request, a stylesheet, or any JavaScript, so no Lighthouse number will move in a way you can see. What you actually get is:

  • One fewer line in your page source and your response headers.
  • One fewer alternate URL (?p=123) being advertised for content that already has a canonical permalink, which is the part I care about. A site that hands out two addresses for the same page is doing duplicate-URL housekeeping it does not need to.

If you are auditing a site for a clean <head>, this belongs on the list next to disabling the emoji scripts and removing the unused feed links. If you are trying to make a genuinely slow page fast, it is not where your afternoon should go. See the broader wp_head cleanup guide for where these small wins fit, and optimising WooCommerce for the levers that actually move load time.

Put it in a plugin, not the theme

The snippet works in functions.php, but theme code vanishes the moment you switch themes, and "why did that tag come back?" is an annoying thing to rediscover later. Drop this into wp-content/mu-plugins/te-remove-shortlink.php instead. Must-use plugins load automatically, before regular plugins, and survive theme switches:

php
<?php
/**
 * Plugin Name: TE Remove Shortlink
 * Plugin URI:  https://techearl.com/remove-wordpress-shortlink
 * Description: Removes the rel=shortlink tag from wp_head and the matching Link: HTTP header.
 * Version:     1.0.0
 * Author:      Ishan Karunaratne
 * Author URI:  https://techearl.com
 * License:     GPL-2.0-or-later
 * Text Domain: te-remove-shortlink
 */

add_action( 'init', function () {
    // The tag in the page <head> on singular views.
    remove_action( 'wp_head', 'wp_shortlink_wp_head', 10 );

    // The Link: <...>; rel=shortlink HTTP response header.
    remove_action( 'template_redirect', 'wp_shortlink_header', 11, 0 );
} );

That is the same two lines with a real plugin header on top, so it shows up in the must-use list with a name and a source rather than as an anonymous file.

If you already run a head-cleanup or SEO plugin, you probably do not need this snippet at all. Perfmatters, WPCode, and Yoast's crawl-optimization settings all expose a shortlink toggle, so flip that instead of adding code that does the same thing twice.

The priority detail people get wrong

This is the same trap as every "remove a core action" snippet, and the shortlink has two priorities to get right, not one.

WordPress registers the two callbacks like this in wp-includes/default-filters.php:

php
add_action( 'wp_head', 'wp_shortlink_wp_head', 10, 0 );
add_action( 'template_redirect', 'wp_shortlink_header', 11, 0 );

remove_action() only matches when the priority you pass equals the priority the action was added with. The head tag is added at the default 10, so removing it works with or without the explicit 10. The header is the one that bites: it is added at priority 11, so remove_action( 'template_redirect', 'wp_shortlink_header' ) with no priority defaults to 10, does not match, and silently leaves the header in place. You strip the visible tag, think you are done, and curl -I still shows rel=shortlink. Pass the 11.

Removing these two actions stops WordPress advertising the shortlink. It does not disable the underlying wp_get_shortlink() function or the ?p=123 URL itself, which still resolves (and should, since old shares may rely on it). That is the right behaviour: you are removing the promotion of an alternate URL, not breaking a URL that already exists in the wild. If a plugin you use genuinely wants shortlinks, leave this off for that site.

Verify it worked

After adding the snippet, hard-refresh a single post and check two things.

The page source, searched for shortlink:

text
$ curl -s https://example.com/sample-post/ | grep -i shortlink
(no output)

And the response headers:

text
$ curl -sI https://example.com/sample-post/ | grep -i 'rel=shortlink'
(no output)

Both empty means it is fully gone. If the header still shows up, it is the priority 11 bug above. If the tag is still there, the usual suspects are a page cache serving old HTML (purge it) or a plugin re-adding the action on a hook that runs after your init callback.

See also

Sources

Authoritative references this article was fact-checked against.

TagsWordPressPerformancePHPfunctions.phpshortlinkwp_head

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts