Make all links have relative URLs in WordPress posts

WordPress’s TinyMCE editor makes all links use absolute URLs with your blog’s siteurl as the base. Your blog’s siteurl is the value in Settings > General Settings > WordPress Address (URL). This value is hardcoded in your posts. This means that if you change your domain, change your permalink structure, or switch from http to https, you’ll have to update your posts. Also, you cannot support both http and https; you have to choose one. This is because whenever someone clicks a link, they’ll go to the version of your site that’s in the siteurl. All of these issues can be fixed using relative URLs.

Here is the code to automatically convert all absolute URLs to relative URLs when you save a post:

add_filter('wp_insert_post_data', function($data) {
    if ($data['post_type'] === 'post') {
        $site_url = site_url();
        $site_url_relative = preg_replace('~^https?://~', '//', $site_url);
        $data['post_content'] = preg_replace("~(<[a-z][^>]+(?:href|src)=\\\\('|\"))$site_url(?=[^\\s'\">]*\\2)~i", "\\1$site_url_relative", $data['post_content']);

    return $data;

I should explain how the regex works. The regex replaces <a href=\"\" with <a href=\"//\". When this function is called, the post content is already escaped, so there’s a backslash before the quotes. It replaces <a>, <img>, <video>, etc. It does not replace HTML tags written as text because the “less than” symbol is encode as an HTML entity.

If you want to use relative URLs relative to your root directory (i.e. make it href=\"/foo\"), replace the first preg_replace with:

$site_url_relative = preg_replace('~^https?://[^/]+~', '', $site_url);

Leave a Reply

Your email address will not be published. Required fields are marked *