How to add a Custom Link to a Menu with an URL that is relative to the blog URL

Part of my work is to create WordPress websites. I usually work on my laptop until I have something good enough to be uploaded to the test server where the client reviews it.

I create a VirtualHost for every new project so I’m always working with a WordPress installation in a domain that looks like, but when the site is uploaded to the test server (not controlled by me), the domain may end being something like

The problem is that if I add a custom link to a menu that points to, for example, /events/, it would work fine locally creating a link to, but in the test server the link will point to http://testserver/events/, which is obviously not right.
What I want is to give the custom link an URL that would work both on my local environment and the test server.

I already handle the problem of changing the home and siteurl WordPress options by:

  • changing those settings on the local database
  • creating a dump of the database
  • update the database on the server
  • restoring the local options.

I don’t want to use full URLs for the custom links and having to replace those with the server URL every time I need to update the server’s database.

For links inside the post content there is a plugin that solve the problem adding two shortcodes:, but I haven’t been able to find something similar for Custom Links.

Solutions Collecting From Web of "How to add a Custom Link to a Menu with an URL that is relative to the blog URL"

I was also looking for a solution for this and I found simple one.

This is what you need to place into the URL field:


enter image description here

It works just fine!

Best Regards

First you have to install this plugin for URL shortcodes.

Add this code to your functions.php file in your theme:

class description_walker extends Walker_Nav_Menu {
    function start_el( &$output, $item, $depth, $args ) {
        global $wp_query;
        $indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';

        $class_names = $value = '';

        $classes = empty( $item->classes ) ? array() : (array) $item->classes;

        $class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
        $class_names = ' class="'. esc_attr( $class_names ) . '"';

        $output .= $indent . '<li id="menu-item-'. $item->ID . '"' . $value . $class_names .'>';

        $attributes  = ! empty( $item->attr_title ) ? ' title="'  . esc_attr( $item->attr_title ) . '"' : '';
        $attributes .= ! empty( $item->target )     ? ' target="' . esc_attr( $item->target     ) . '"' : '';
        $attributes .= ! empty( $item->xfn )        ? ' rel="'    . esc_attr( $item->xfn        ) . '"' : '';

        // echo $item->url;
        $string = explode( '::', $item->url, 3 );
        if ( $string[1] ) {
            $string[1] = str_replace( '-', ' ', $string[1] );
            $item->url = do_shortcode( "[$string[1]]" ); 

        $attributes .= ! empty( $item->url )        ? ' href="'   . esc_attr( $item->url        ) .'"' : '';

        $prepend = '<strong>';
        $append = '</strong>';
        $description  = ! empty( $item->description ) ? '<span>' . esc_attr( $item->description ) . '</span>' : '';

        if ( $depth != 0 ) {
            $description = $append = $prepend = "";

        $item_output  = $args->before;
        $item_output .= '<a'. $attributes . '>';
        $item_output .= $args->link_before . $prepend . apply_filters( 'the_title', $item->title, $item->ID ) . $append;
        $item_output .= $description . $args->link_after;
        $item_output .= '</a>';
        $item_output .= $args->after;

        $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );

Then you have to call the wp_nav_menu function from the templates files:

$arg = array( 
    'menu'        => "main-menu", 
    'echo'        => true, 
    'fallback_cb' => 'wp_page_menu', 
    'depth'       => 0, 
    'walker'      => new description_walker() 
wp_nav_menu( $arg );

That’s it. Then go to the back-end menu section.

For example, if I want to give the page URL to a custom link, I will add it like this:


Now you can go to the front-end and check that the shortcode works.

Using <base href=" "> tag in the head meta will give a base url to all the relative anchors in the page.


12.4 Path information: the BASE element

Relative custom links in wordpress:
If you want the site url to be the base url of all anchors add this to the theme / header.php within the <head> :

<base href="<?php echo site_url(); ?>/">

i know could be late for you but could help someone else.

On a custom URL in the Menu setup it is possible to use relative links to the [blogurl]. The secret is to start the relative URL with a single /. When a single / starts the custom URL the system will not prepend the typical http:// and then the current blogURL will be generated in the target URL at execution time.

If you want to go to your home page, simply put / as the custom URL

If you want to go to the index page in the folder bbforums then put /bbforums as the custom URL.

This allows you to move a site to a test domain without having to hard code the new blogURL in all the custom links for the menus.

For Example:
If my blog is and I want to test it in a subdomain the site can be moved between test and production without menu problems using the relative URL convention noted above. I have successfully tested this approach using the XCloner plugin to move the site.

You can use the nav_menu_link_attributes filter to inspect and modify the href attribute for each menu item before it is output.

In this example, we look for any href attributes that start with a /, and prepend the test site URL in that case:

function wpd_nav_menu_link_atts( $atts, $item, $args, $depth ){
    if( '/' == substr( $atts['href'], 0, 1 ) ){
        $atts['href'] = '' . $atts['href'];
    return $atts;
add_filter( 'nav_menu_link_attributes', 'wpd_nav_menu_link_atts', 20, 4 );

You could create a simple plugin with this code and activate it only on your test server, or create some sort of flag that conditionally applies this filter when the test site environment is present.