๐ Check out this awesome post from Hacker News ๐
๐ **Category**:
๐ **What Youโll Learn**:
In this guide, youโll learn how to host your blog on a subdirectory (e.g. example.com/blog) instead of a subdomain (e.g., blog.example.com). Every step here has been tested and verified to work.
Introduction
Hosting your blog on a subdirectory can improve SEO and enhance user experience.
Although there are a lot of articles that espouse the benefits of using subdirectories over subdomains, few resources that provide a step-by-step guide on how to actually set this up.
Why Host on a Subdirectory?
The benefits to hosting on a subdirectory is primarily to improve SEO.
There are a lot of other articles out there on this topic, but they all say something similar to the following:
-
Hosting your blog on a subdirectory is better for SEO because it consolidates your websiteโs authority and ranking power.
-
Google has stated that they do not treat subdomains as a separate entity.
-
Despite what Google has stated, empiric data suggests that subdirectories outperform subdomains in search rankings.
-
If you want to maximize your SEO efforts, hosting on a subdirectory is the way to go.
If you want to learn more, you can read this article by ButterCMS: Blog Subdomain or Subdirectory? Hint: One is 40% Better.
My personal experience has been similar. When I moved a blog from a subdomain to a subdirectory, I saw a noticeable increase in organic traffic and search engine rankings. The increase happened after a few weeks. During that time, I did not release any new content and nor did I promote the blog.
Why Not Host on a Subdirectory?
The setup is more complex. Many blogging platforms and CMSs are designed to work on subdomains, and configuring them to work on a subdirectory can be tricky.
Iโve personally found the setup process to be quite time consuming. Itโs a tricky process and you have to follow the instructions carefully. After having previously changed a blog from a subdomain to a subdirectory, Iโve found difficult to justify the time to do it for taikohub.com.
If you still think itโs worth your time, then read on.
Steps to Host Your Blog on a Subdirectory
Lets suppose you have two sites right now. One is example.com and the other is blog.example.com. You want to host the blog on example.com/blog instead of blog.example.com.
Lets also suppose your blog (blog.example.com) is a Next.js app hosted on Vercel and your main site (example.com) is a static site hosted on Render.
Although Vercel and Render are used as examples here, the steps are nearly identical for other hosting providers. You do not need configure anything for your hosting provider. Everything can be done from the Cloudflare Dashboard, and from the comforts of your text editor.
Important: Note that Cloudflare often changes their dashboard UI and routes. If you find that the steps here do not match what you see on your Cloudflare Dashboard, just use the search function in the dashboard to find the relevant section.
Step 1: Set Up DNS Records for the Main Site
First, set up the DNS records for your main site (example.com). Again, if you do not use Render, then follow the equivalent steps for your hosting provider. Generally this should be in their documentation.
-
Go to your Cloudflare Dashboard. Click into your domain, then SSL/TLS, then Overview. Then click โConfigureโ.

-
Next, select โCustom SSL/TLSโ then select โFullโ.

-
Go to DNS records in the sidebar by clicking on โDNSโ, then โDNS Recordsโ. Then click โAdd Recordโ.

-
Add the following DNS Records. Replace
my-site.onrender.comwith the service URL for your main site. If you have other applications such as an API, you can add those as well. Note that itโs important you set the โProxy statusโ to โProxiedโ. Itโs also important you do NOT add a wildcard record (eg.*.example.com).Type Name Target Proxy status TTL CNAME @ my-site.onrender.com Proxied Auto CNAME www my-site.onrender.com Proxied Auto CNAME api my-api.onrender.com Proxied Auto
Step 2: Set Up DNS Records for the Blog
-
Make sure your blog is already accessible on a subdomain (eg. blog.example.com).
-
Add another DNS Record for the blog. Replace
cname.vercel-dns.comwith the CNAME target provided by the hosting provider for your blog.Type Name Target Proxy status TTL CNAME example.com my-site.onrender.com Proxied Auto CNAME www my-site.onrender.com Proxied Auto CNAME api my-api.onrender.com Proxied Auto CNAME blog cname.vercel-dns.com Proxied Auto
Step 3. Configure Your Next.js Blog
Ensure Correct Routing for Static Assets
-
Make sure that your Next.js blogโs router points to
/and not/blog. You should NOT have any routes that contain/blog. Edit thenext.config.jsornext.config.mjsfile and addbasePath: "https://www.davidma.org/blog"to the config./** @type โก */ const nextConfig = { basePath: "https://www.davidma.org/blog", // Add this line images: ๐ฅ, redirects: async () => ๐ฅ, }; export default nextConfig;
Step 4. Add a Cloudflare Worker
-
Go to Cloudflare Dashboard. Click โWorkers & Pagesโ. Click โCreateโ then click โCreate Workerโ.

-
For the purpose of this blog post, weโll go with the easiest option by selecting โStart with Hello World!โ. For production applications, consider using Git. It looks like the following. Lets name it
blog-worker. Then click โDeployโ.// worker.js /** * Welcome to Cloudflare Workers! This is your first worker. * * - Run "npm run dev" in your terminal to start a development server * - Open a browser tab at http://localhost:8787/ to see your worker in action * - Run "npm run deploy" to publish your worker * * Learn more at https://developers.cloudflare.com/workers/ */ export default { async fetch(request, env, ctx) { return new Response('Hello World!'); }, }; -
Now replace the code with the following:
export default { async fetch(request, env, ctx) { async function MethodNotAllowed(request) { return new Response(`Method ${request.method} not allowed.`, { status: 405, headers: { Allow: "GET", }, }); } // Only GET requests work with this proxy. if (request.method !== "GET") return MethodNotAllowed(request); // Get the URL that was just requested. const url = new URL(request.url); // Swap out the subdirectory with the subdomain to request the actual URL. const originUrl = url.toString().replace( 'https://example.com/blog', 'https://blog.example.com/blog' ).replace( 'https://www.example.com/blog', 'https://blog.example.com/blog' ); // Fetch the origin. const originPage = await fetch(originUrl); // Return the subdomain, as the subdirectory. const newResponse = new Response(originPage.body, originPage); return newResponse; }, }; -
Change the URLs as needed. To save, click on the version ID hash (eg.
b30983e0) then click โApplyโ. -
To deploy the changes, go to the worker dashboard. Click โDeploymentsโ. Look under โVersion Historyโ. Click โโฆโ then โDeployโ on the latest version.
Version ID Created Version & Git Message Source vb29485e0 3minโฆ Update to โฆ Dashboard โโฆโ vf859f2e0 2hโฆ Updated Script Dashboard โโฆโ
Step 5. Connect Next.js Site with Cloudflare Worker
-
Go to โWorker Routesโ in the Cloudflare Dashboard sidebar. Click on โAdd Routeโ.

-
Add the following route for the blog content:
- Route:
example.com/blog* - Worker:
blog-worker. This is the worker you just created.
- Route:
-
Add another route for the static assets:
- Route:
example.com/blog/_next/static* - Worker:
blog-worker
- Route:
You should now be able to access your blog at example.com/blog. If this works, congratulations! Youโve successfully hosted your blog on a subdirectory using Cloudflare Workers.
Configure Search Engine Robots.txt in Your Next.js App
-
Now that youโve successfully hosted the blog on the subdirectory, you need to make sure search engines donโt index the subdomain. This is because the blog is already indexed on the subdirectory. If search engines index both, then you may run into SEO issues due to duplicate content.
-
Update your
next.config.jsornext.config.mjsfile./** @type {import('next').NextConfig} */ const nextConfig = { basePath: "https://www.davidma.org/blog", images: { remotePatterns: [ { protocol: "https", hostname: "imagedelivery.net", }, ], }, redirects: async () => { // Add this block return []; }, async headers() { // Add this block return [ { source: '/:path*', headers: [ { key: 'X-Robots-Tag', value: 'noindex, nofollow', }, ], }, ]; }, }; export default nextConfig; -
Now update your cloudflare worker.
export default { async fetch(request, env, ctx) { async function MethodNotAllowed(request) { return new Response(`Method ${request.method} not allowed.`, { status: 405, headers: { Allow: "GET", }, }); } // Only GET requests work with this proxy. if (request.method !== "GET") return MethodNotAllowed(request); // Get the URL that was just requested. const url = new URL(request.url); // Swap out the subdirectory with the subdomain to request the actual URL. const originUrl = url.toString().replace( 'https://example.com/blog', 'https://blog.example.com/blog' ).replace( 'https://www.example.com/blog', 'https://blog.example.com/blog' ); // Fetch the origin. const originPage = await fetch(originUrl); // Return the subdomain, as the subdirectory. let newResponse = new Response(originPage.body, originPage); // Remove "noindex" from the origin domain. newResponse.headers.delete("x-robots-tag"); return newResponse; }, };
Step 6. Verify Your Subdomain is Not Indexed
-
Open your appโs deploy URL. This may look something like
https://vercel.com/my-projects-30d8ek3n/my-blog/d934nfid9823sbsNgoMnOOnsiKxn. -
Open the browserโs Network tab in Developer Tools.
-
Check for existence of an โX-Robots-Tagโ header. If itโs not there, then your Next.js app is correctly configured to not be indexed.
Step 7. Verify Your Subdirectory is Indexed
{๐ฌ|โก|๐ฅ} **Whatโs your take?**
Share your thoughts in the comments below!
#๏ธโฃ **#Host #Blog #Subdirectory #Subdomain #Cloudflare #Workers**
๐ **Posted on**: 1776466971
๐ **Want more?** Click here for more info! ๐
