How to Migrate a Subdomain or a Domain to a Subdirectory on a Different Server

How to Migrate a Subdomain or a Domain to a Subdirectory on a Different Server

When we rebranded Publer, we decided to split the website, the platform, and the blog from one another as they're built by different teams using multiple technologies and services:

  • The website (publer.io) is built using NextJS and hosted on Vercel
  • The platform (app.publer.io) is built using ReactJS, Ruby on Rails, MongoDB / PostgreSQL, and hosted on AWS
  • The blog (blog.publer.io) is hosted on WordPress

Everything worked as expected, at least in terms of usability:

However, after an audit with Granit Doshlaku from Manaferra , they brought to our attention that having the blog, our primary source of content, as a subdomain versus a subdirectory was truly hurting our search ranking (despite the rumors).

A subdomain is generally considered as a standalone site that is branched off from the main domain.

In a few words, we were competing with ourselves for the traffic!




Why couldn't we just move the blog to a subdirectory?

Our main website is hosted on Vercel, so we cannot serve the WordPress blog content under publer.io/blog. Adding (JavaScript) redirects wouldn't make any difference in SEO.

In order to have everything under the same roof, my initial thoughts were the following:

  • Rebuild the main website using WordPress and have everything non-platform related hosted on WordPress.
  • Build our own blog system using NextJS and have everything hosted on Vercel.

Both solutions were very costly and not feasible in the long run. We're not a team of WordPress developers and we cannot reinvent WordPress from scratch.

I was confident there was a better way out there, something that would route the traffic before it even reached our servers.

After hours of research, I stumbled upon this article by John S. and the fun began!




Solution: Cloudflare Workers

We were already using Cloudflare for SSL, content delivery network services, and DDoS mitigation, so they weren't new to me.

In order for this solution to work, your DNS needs to be routed through Cloudflare and the traffic to be proxied (the clouds must be orange).


No alt text provided for this image
DNS routing and proxying through CloudFlare


This should be straightforward, but just in case I'm providing some links:




How to set up Cloudflare Workers

Cloudflare Workers is a serverless JavaScript platform that will intercept and?process HTTP requests before they travel all the way to your original server.

No alt text provided for this image
Cloudflare Workers have a free limited plan and their paid plans start at only $5/month.

In our case, we have to intercept requests made to publer.io/blog (the main website) and have them return the content from our WordPress blog.

We also need another worker to intercept requests made to blog.publer.io and redirect them to the /blog subdirectory while still being able to log into WordPress with /wp-admin (or any other custom login endpoint).


No alt text provided for this image
You can go to Workers & Pages on your Cloudflare dashboard and create an application.




The blog-router worker

This worker will simply intercept the requests that go to https://publer.io/blog* and route them through the WordPress site.

Simply paste the following JavaScript code when prompted and don't forget to replace the URLs with your own:

const targetUrl = 'https://publer.io/blog';
const sourceUrl = 'https://blog.publer.io';

async function handleRequest(request) {
  const url = new URL(request.url);
  const originUrl = url.toString().replace(targetUrl, sourceUrl);
  const originPage = await fetch(originUrl);
  const newResponse = new Response(originPage.body, originPage);
  return newResponse;
};

addEventListener('fetch', function(event) {
  event.respondWith(handleRequest(event.request))
})        

If you pay attention, this is not a redirect. This worker will simply fetch and serve the content from blog.publer.io when the user browses publer.io/blog.

Once you have deployed the worker, you need to make sure to add the following route, otherwise, it won't get triggered.


No alt text provided for this image
Remember to replace the values with your own domain


This worker right here will be enough for you to serve content from blog.publer.io to publer.io/blog, but the old subdomain will still be accessible by users and search engines. Let's redirect the traffic!




The blog-redirect worker

This worker will intercept requests that go to https://blog.publer.io/* and redirect them when needed. Follow the same steps for creating a new application and paste the following JavaScript code when prompted.

Don't forget to replace the URLs with your own:

const baseUrl = 'https://publer.io/blog'
const statusCode = 301;

async function handleRequest(request) {  
  const url = new URL(request.url);
  const { pathname, search } = url;
  
  let wpRoutes = pathname.startsWith('/_static') || pathname.startsWith('/wp-') || pathname.startsWith('/xmlrpc.php') || pathname.startsWith('/.well-known');
  let wpQueries = search.startsWith('?rest_route') || search.startsWith('?p=') || search.startsWith('?preview_id=') || search.startsWith('?preview=');

  if(wpRoutes || wpQueries) {
    // Don't redirect as it will break WP dashboard
    const response = await fetch(request);
    return response;
  } else {
    const destinationURL = baseUrl + pathname + search;
    return Response.redirect(destinationURL, statusCode);
  }
};

addEventListener('fetch', async event => {
  event.respondWith(handleRequest(event.request));
});        

This worker will make sure that the traffic that goes to the blog subdomain will be a 301 redirect to the blog subdirectory unless you're accessing your WordPress dashboard.

Once you have deployed the worker, you need to add the following route, otherwise, it won't get triggered.


No alt text provided for this image
Remember to replace the values with your own domain


These two workers allow us to have all the visitors and search traffic go to the main domain publer.io/blog, while we continue to log in to our WordPress dashboard using blog.publer.io/wp-admin

But we're not done yet!




Sitemaps and Internal Links

Everything looks good until you take a look at the sitemaps and meta tags!

While no one else accesses the old blog subdomain, you will notice that search engines still do, which defeats the purpose.

After days of research, I finally found a WordPress plugin that did the job. It's called Better Find and Replace and it's free.

Once you install and activate it, add the following rule:

No alt text provided for this image
Remember to replace the values with your own domain
Find: https:\/\/blog\.publer\.io\/(?!_static)
Replace with: https://publer.io/blog/
Ryle's Type: Regular Expression - Managed
Where To Replace: All over the website        

This plugin will replace every link that you reference internally when writing an article or when search engines crawl through your sitemap.

No alt text provided for this image
Writing articles on WordPress continues as usual
Even though we use blog.publer.io links internally, visitors and search engines will always, always see publer.io/blog links.


Update: If your site is a non-WordPress one, thus you cannot use the Better Find and Replace plugin, the same outcome can be achieved using Cloudflare's HTMLRewriter.

const oldUrl = 'https://blog.publer.io';
const newUrl = 'https://publer.io/blog';

async function handleSitemapRequest(request) {
  const response = await fetch(request);
  return textRewriter.transform(response);
}

async function handleRequest(request) {
  const response = await fetch(request);
  return htmlRewriter.transform(response);
}

class TextHandler {
  text(text) {
    text.replace(text.text.replace(oldUrl, newUrl));
  }
}
 
class AttributeRewriter {
  constructor(attributeName) {
    this.attributeName = attributeName;
  }
 
  element(element) {
    const attribute = element.getAttribute(this.attributeName);
    if (attribute) {
      element.setAttribute(
        this.attributeName,
        attribute.replace(oldUrl, newUrl)
      );
    }
  }
}

const textRewriter = new HTMLRewriter()
  .on("*", new TextHandler());
 
const htmlRewriter = new HTMLRewriter()
  .on("link", new AttributeRewriter("href"))
  .on("meta", new AttributeRewriter("content"));

addEventListener('fetch', async event => {
  let url = event.request.url;
  if(url === 'https://publer.io/blog/sitemap.xml') {
    event.respondWith(handleSitemapRequest(event.request));
  }
  else {
    event.respondWith(handleRequest(event.request))
  }
});        

Since sitemaps are not HTML files, you will have to do a regular text replacement, but since this may be slower, I would only run this script on the sitemap(s).




1500x Increase in Organic Traffic

After making sure that all technical issues were resolved, we partnered with Chris A Tweten and Amanda Laine from Spacebar Collective to 10-20x the content produced.

The result speaks for itself:

No alt text provided for this image
1500x Increase in Organic Traffic




Why didn't we migrate the app subdomain?

  • The app subdomain is not indexed by search engines.
  • We are more interested in landing visitors to our main website than the login-protected platform.
  • Publer users authenticate to all major social networks through the platform, which means that if Facebook accidentally blocks our blog for content that goes against their community guidelines, the authentication will no longer work.




How to Migrate a Domain into a Subdirectory of Another Domain

The CEO of Kalemi Travel & Tours and my brother Marsel Kalemi recently asked me to migrate kalemitravel.com into kalemi.com/udhetime.


What I thought would take a few minutes of my time, took more than 24 hours.

Please follow these steps so that you don't make the same mistakes as I did.


Move the Domain into a Subdomain of the new Domain

I wasted a lot of time trying to keep the old WordPress domain, but I kept running into 403 Forbidden errors, infinite redirects, and WordPress update errors.

Life would have been easier if I immediately changed the WordPress and site address to a subdomain of the new domain.

In my case, I changed it from kalemitravel.com to travel.kalemi.com.

This step should be straightforward by going to WordPress General Settings, but for a more detailed guide, click here.

Move the domain into a subdomain of the new domain

Please verify that everything is working as expected before moving to the next step.


Set up Cloudflare workers

We are now back to the original scenario, migrating from a subdomain to a subdirectory within the same domain.

The steps are similar, with some minor tweaks as we have to redirect two routes, kalemitravel.com and travel.kalemi.com, into the new subdirectory kalemi.com/udhetime.


  1. The router worker will be identical to the one explained at the beginning of the article (in this case it will be triggered by https://kalemi.com/udhetime* and it will listen for https://travel.kalemi.com
  2. Similarly, the redirect worker will redirect traffic to https://travel.kalemi.com, but it will be triggered by two routes, the old domain and the subdomain we just created.

Add both routes as triggers for the redirect worker
All visitors and search traffic will now go to the new subdirectory kalemi.com/udhetime, while the new WordPress login page will be under the subdomain travel.kalemi.com/wp-admin


Sitemaps and Internal Links

Again I had to install Better Find and Replace and add a similar rule as above.

This regex will replace in realtime all links, except the WordPress-related ones, that are under kalemitravel.com or travel.kalemi.com
Find: https?:\/\/(travel\.)?kalemi(travel)?\.com\/(?!wp\-)
Replace with: https://kalemi.com/udhetime/
Ryle's Type: Regular Expression - Managed
Where To Replace: All over the website        




Let me know if you need help setting everything up or would like to know more. I'm more than happy to do it for free, as long as you have attempted it on your own first.

Using the same steps I was able to recently migrate:

Don't forget to like, share, and subscribe to my newsletter.

I will be sharing more technical articles in the future!

Ervin Kalemi

Empowering your business's online presence with Publer and the upcoming Kibo

1 年

If your site is a non-WordPress one, thus you cannot use the Better Find and Replace plugin, I learned today that the same outcome can be achieved using Cloudflare's HTMLRewriter: https://developers.cloudflare.com/workers/runtime-apis/html-rewriter/ More info in the updated article ^

回复
Ervin Kalemi

Empowering your business's online presence with Publer and the upcoming Kibo

1 年

Had to revisit this article as it helped me migrate: plans.publer.io/professional???publer.io/plans/professional?(Framer & Vercel) plans.publer.io/business???publer.io/plans/business?(Framer & Vercel) kalemitravel.com???kalemi.com/udhetime?(WordPress & Bokun) b2b.kalemi.com???kalemi.com/b2b?(WordPress & Bokun)

回复
Granit Doshlaku

Co-founder, Chief Strategy Officer at Manaferra (Higher Ed. SEO Agency)

1 年

loved working with you guys on this, the growth has been phenomenal ????

Noel Ceta

Founder @Apollo Digital. SEO & Content Expert

1 年

Great post. I did suggest to Jorgo about a year ago that you should be doing this though :D

Niko Moritz

Gib deiner Marke eine Boost mit storydriven Content. Ich helfe Dir, dein Publikum nicht nur zu erreichen, sondern zu begeistern.

1 年

要查看或添加评论,请登录

社区洞察

其他会员也浏览了