Using relative URLs in WordPress

Relative URLs has some benefits: smaller page size (and thus download time) and making the site more portable when you move to another domain. The 2nd problem often happens and it can make your post links broken. Although it can be solved using some backup & restore plugin like BackupBuddy, or using Search and Replace plugin, we better avoid it before it happens. This post will show you how to make your WordPress site support relative URLs.

Note: The topic of relative URLs in WordPress is not new, some people discussed in WP.org forum, and there’s also a solution. What I’m trying to do in this post is making a better, more completed solution.

Remove protocol and domain from absolute URLs

Basically, we think about a string replacement: replace home_url() by / from absolute URLs, and we can write a simple function like this:

function rw_remove_root( $url ) {
    $url = str_replace( home_url(), '', $url );
    return '/' . ltrim( $url, '/' );
}

But do you know that WordPress has a built-in function for this purpose? It’s wp_make_link_relative. The function is in /wp-includes/formatting.php file. Here it is:

function wp_make_link_relative( $link ) {
    return preg_replace( '|https?://[^/]+(/.*)|i', '$1', $link );
}

Pretty simple. What it does is removing protocol and domain name from the absolute URLs (without using home_url).

Making WordPress relative URLs

Now what we have to do is apply this function to post, category, term, archive, … links. Fortunately, WordPress always uses filters in getting URL functions. All of them are in wp-includes/link-template.php and wp-includes/taxonomy.php files. So, we’ll add wp_make_link_relative filter to those functions. Here’s the the code that you can put in functions.php file of your theme or in your plugin:

add_filter( 'post_link', 'wp_make_link_relative' );       // Normal post link
add_filter( 'post_type_link', 'wp_make_link_relative' );  // Custom post type link
add_filter( 'page_link', 'wp_make_link_relative' );       // Page link
add_filter( 'attachment_link', 'wp_make_link_relative' ); // Attachment link
add_filter( 'get_shortlink', 'wp_make_link_relative' );   // Shortlink

add_filter( 'get_pagenum_link', 'wp_make_link_relative' );          // Paginated link
add_filter( 'get_comments_pagenum_link', 'wp_make_link_relative' ); // Paginated comment link

add_filter( 'term_link', 'wp_make_link_relative' );   // Term link, including category, tag
add_filter( 'search_link', 'wp_make_link_relative' ); // Search link

add_filter( 'post_type_archive_link', 'wp_make_link_relative' ); // Post type archive link

// Date archive link
add_filter( 'day_link', 'wp_make_link_relative' );
add_filter( 'month_link', 'wp_make_link_relative' );
add_filter( 'year_link', 'wp_make_link_relative' );

Or better code with simple improvements:

$filters = array(
    'post_link',       // Normal post link
    'post_type_link',  // Custom post type link
    'page_link',       // Page link
    'attachment_link', // Attachment link
    'get_shortlink',   // Shortlink

    'post_type_archive_link',    // Post type archive link
    'get_pagenum_link',          // Paginated link
    'get_comments_pagenum_link', // Paginated comment link

    'term_link',   // Term link, including category, tag
    'search_link', // Search link

    'day_link',   // Date archive link
    'month_link',
    'year_link',
);

foreach ( $filters as $filter ) {
    add_filter( $filter, 'wp_make_link_relative' );
}

After doing that, whenever you show a link in a theme template file, for ex. using the_permalink() function, the corresponding filters will be applied (in this case: post_link), including our wp_make_link_relative function. That means all URLs in your WordPress site will be changed relative URLs automatically without touching to template files. Enjoy relative URLs!

Relative URLs in feed or sitemap

(Updated on 12/06/2012)

After making this technique live on Deluxe Blog Tips, I found some problems:

  • The URLs in feed are relative, which is not expected. This was reported by Jared.
  • If you’re using WordPress SEO plugin by Yoast, the URLs in sitemap are relative, too, which causes some problems in Google Webmaster Tool.

So, I update the code to make the filters are applied only when we need. I might miss some cases when it doesn’t work, and hope to hear your feedback:

add_action( 'template_redirect', 'rw_relative_urls' );

function rw_relative_urls() {
    // Don't do anything if:
    // - In feed
    // - In sitemap by WordPress SEO plugin
    if ( is_feed() || get_query_var( 'sitemap' ) )
        return;

    $filters = array(
        'post_link',
        'post_type_link',
        'page_link',
        'attachment_link',
        'get_shortlink',
        'post_type_archive_link',
        'get_pagenum_link',
        'get_comments_pagenum_link',
        'term_link',
        'search_link',
        'day_link',
        'month_link',
        'year_link',
    );
    foreach ( $filters as $filter )
    {
        add_filter( $filter, 'wp_make_link_relative' );
    }
}

I’m using relative URLs in Deluxe Blog Tips, and will update this post if I found any bugs.

25 Comments

  1. Hi! Thanks for the useful article! But i cant add filter for the_post_thumbnail. Added in array “the_post_thumbnail”, but this dont work. How remove absolute URL for thumbnail?

    Reply
  2. I am using WordPress 3.3.1 and trying to put the following in my functions.php page for my theme (in order to implement the relative links feature). But it does not work.

    /***************************************************************/
    add_action(‘template_redirect’, ‘rewrite_relative_urls’);
    function rewrite_relative_urls() {
    echo “Testing template_redirect”; // this part works

    if ( is_feed() || get_query_var( ‘sitemap’ ) )
    return;

    add_filter( ‘post_link’, ‘wp_make_link_relative’ ); // Normal post link
    add_filter( ‘post_type_link’, ‘wp_make_link_relative’ ); // Custom post type link
    }
    /***************************************************************/

    However, if I do not use add_action hook for template_redirect to call the above function and simply add the following filters, the relative links feature works.

    add_filter( ‘post_link’, ‘wp_make_link_relative’ ); // Normal post link
    add_filter( ‘post_type_link’, ‘wp_make_link_relative’ ); // Custom post type link

    Do you know why?

    Reply
  3. imho to many hook…
    This should be enough: add_filter( ‘home_url’, ‘wp_make_link_relative’ );

    … it seems to work well

    Reply
  4. I’m a bit confused by the home_url() reference in there – can you explain it? I was expecting it to make home_url references in the template rewrite to “/”, but it isn’t for me it isn’t – Im still getting the domain.

    Reply
    • Hi, `home_url()` returns the URL of your homepage, not the domain. So, replacing it with `/` will make the URL relative. What are you confused here?

      Reply
  5. Regarding the problem of making the site more portable when you move to another domain, there is another solution that I use all the time to move sites from development URLs to live sites. It’s the WP Migrate DB plugin: http://wordpress.org/extend/plugins/wp-migrate-db/. It does a global search/replace on the database, changing not just URLs but file paths and string lengths on serialized data.

    Reply
    • Thanks for mentioning that plugin. My first choice always is BackupBuddy. The second is Search and Replace. I used it (Search & Replace) several times and it worked nicely. Looks like that the WP Migrate DB can do the migration automatically, which is awesome. I’ll give it a try when I have a chance. Thanks.

      Reply
  6. I’m gone to tell my little brother, that he should also pay a quick visit this web site on regular basis to take updated from latest gossip.

    Reply
  7. I used to use relative links, but when people would view articles in web based rss feed readers, the links would stay relative to the reader, e.g. google.com/my-relative-link. Any suggestions on how to deal with that?

    Reply
  8. Thanks for the useful article!
    But how does it comport with url changing plugins, like wpml?

    Reply
  9. Great tip for the url’s – thanks!

    And I juts wanted to give another thumbs up on your comments form layout. LOve to see an article on this, also – it is too sweet. Great work – thanks again. :)

    Reply
  10. Great article! I actually created a plugin, http://wordpress.org/extend/plugins/roots-plug/ that includes this functionality (among other things) which is based off of the Roots Theme — but since the preg_replace() syntax is a bit different, I’d be interested in seeing if this way works any better. Currently, the relative URL function has an issue when WordPress is installed more than one-level deep – do you know if this does as well? Thanks!

    Reply
    • This technique still works then (with my latest method, not the method of using `home_url`). I’ve just looked at your code, and see you’re using `site_url`. Maybe it’s the problem.

      Reply
  11. Correction in my last post, please. The “get_site_url()” function didn’t arrive until version 3.0, not 2.9.2, and it offers a few extra features to site_url(). (However, I still don’t use it because I find site_url() fits most of my needs with interacting with the site URL.)

    Reply
    • Thanks for your comment!

      In terms of replacing domain name, both `site_url` and `home_url` works well. In my understand, the difference comes *only* when you put WP in a folder.

      BTW, I don’t use `home_url` at the end, because WP already has a function for that purpose, that function works in all cases and WP-independent.

      Reply
  12. You probably should use site_url() instead of home_url(). I interacted with a core WP developer to get the scoop on the differences between site_url() and home_url() and I document it here in [this answer on wordpress.stackexchange.com][1]. You could also expand this article to discuss things like the global constants that WP has for URLs and paths, as well as the functions that WP has for URLs and paths. For instance, in plugin development, I use plugins_url() function a lot, and it’s quirky. As for global constants, WP has a ton of them such as ABSPATH, WP_CONTENT_DIR, and so on. Last, you may want to add some cautionary notes about using some of these functions and constants with older versions of WordPress. For instance, I think site_url() works in 2.8 but get_site_url() didn’t arrive until 2.9.2. In general, you’ll find some of these functions and constants regarding paths aren’t there until WP 2.8, which is why my plugins that I build require at least WP 2.8 or greater.

    [1]: http://wordpress.stackexchange.com/questions/20294/whats-the-difference-between-home-url-and-site-url/50605#50605

    Reply
  13. One of my clients where asking about this a few months back. Wish I found this back then,

    Thanks

    Reply
    • Glad you like this comment form. I take the idea from stackexchange.com sites and the code is still in test. I want receive more feedback from commenters before writing about it.

      Reply

Leave a Reply