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.

It is the ?p=123 style URL WordPress advertises for each post and page, both as a <link rel='shortlink'> tag in the <head> and as a Link: ...; rel=shortlink HTTP header. It dates to WordPress 3.0 and was meant for sharing tools.

In practice very little consumes it now. Social platforms shorten URLs themselves, so for most sites it is just a second, uglier address advertised for a page that already has a clean canonical permalink.

No. The snippet only stops WordPress advertising the shortlink in the head and the HTTP header. The underlying ?p=123 URL still resolves to the post, which is what you want, since old shared links may depend on it.

Because the header is a separate action at a different priority. wp_shortlink_header is hooked to template_redirect at priority 11, and remove_action() only matches if you pass that exact priority. Omit it and the call defaults to 10, does not match, and the header stays. Pass 11.

Prefer a must-use plugin at wp-content/mu-plugins/te-remove-shortlink.php. It loads automatically and survives theme switches, so the tag and header do not silently come back when you change themes. The functions.php version works identically but is tied to the active theme.

It is a small positive at most. The shortlink is an alternate URL for content that already has a canonical permalink, and search engines handle that fine via the canonical tag, so removing it is not a ranking fix. The value is housekeeping: fewer advertised URLs for the same page and a cleaner head. Do not expect it to move rankings on its own.

See also

Sources

Authoritative references this article was fact-checked against.

TagsWordPressPerformancePHPfunctions.phpshortlinkwp_head

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Software Systems Architect · Senior Software Engineer · Engineering Leadership

Software systems architect and senior software engineer with more than two decades designing, building, and running production software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Now a CTO, though what I write here is drawn from the full arc of that work, across architecture, engineering, and operations, not any single job.

Keep reading

Related posts