Subdirectory Blog Hosting: How to Set Up yoursite.com/blog on Any Stack (2026)

yoursite.com/blog

A subdirectory blog needs exactly one piece of infrastructure: a routing rule on your main domain that forwards /blog and /blog/* to wherever the blog is actually hosted, and returns the response without changing the URL in the address bar. That rule is a reverse proxy. It lives in next.config.js, vercel.json, netlify.toml, an Nginx location block, or a Cloudflare Worker, depending on what serves your main site. Nothing else about your site changes.

Why bother: keeping the blog on the same host consolidates internal links, crawl context, and accumulated domain authority instead of splitting them across two properties. The full argument, including the cases where a subdomain is the better call, is in subdomain vs subdirectory. This guide assumes you have made that call and want the wiring.

Where that rule lives, and how it strips the /blog prefix, depends entirely on what serves your main domain:

PlatformWhere the rule livesRule typeHandles prefix strip via
Next.jsnext.config.jsbeforeFiles rewrite:path* in destination
Vercelvercel.jsonrewrite:path*
Nginxserver blockproxy_passtrailing slash on the proxy_pass URI
CloudflareWorker on a routefetch to upstream URLpathname.slice()
Netlifynetlify.toml200 redirect:splat

What a subdirectory blog requires technically

Your main site is served from one origin. Your blog is served from another. The proxy sits in front of both and splits traffic by path.

There are four details that decide whether the setup works or quietly breaks your SEO.

The prefix gets stripped before the upstream fetch

Most hosted blog platforms serve your blog at the root of an origin they give you, something like yourblog.superblog.click/, which is the origin hostname Superblog assigns to each blog and the placeholder used in every snippet below. Your visitors ask for yoursite.com/blog/post-name/. So the proxy has to remove /blog before it fetches upstream:

yoursite.com/blog/post-name/  ->  yourblog.superblog.click/post-name/
yoursite.com/blog/            ->  yourblog.superblog.click/

Every config below does this. The one thing that must not happen is a double prefix (/blog/blog/post-name/), which is what you get when the rule forwards the path untouched to an origin that already serves at root.

The Host header decides which site the origin serves

Proxying to a shared platform means the origin is serving hundreds of blogs from the same edge. It picks yours by the Host header. Proxies differ on what they send: set it explicitly rather than inheriting a default. An origin that receives yoursite.com has no blog matching that name, so you get a 404 or the wrong blog. On Nginx you set the header inside the location block. On Cloudflare Workers, constructing a new Request against the upstream URL sets it for you. Platform-level rewrites on Next.js, Vercel, and Netlify handle it as part of the rewrite.

Trailing slashes have to match on both sides

/blog/post-name and /blog/post-name/ are two different URLs. If both return a 200, you have manufactured a duplicate and split link equity between them. Pick whichever shape your blog publishes, redirect the other permanently, and make sure the proxy does not rewrite one into the other silently. Next.js is explicit about this: if you set trailingSlash: true, the trailing slash has to appear in the rewrite source as well. The redirect half of that advice only applies where you can actually issue one: Nginx and .htaccess can return a 301, but the Next.js, Vercel, and Netlify rules here are rewrites, so on those stacks the self-referencing canonical tag is what consolidates the two shapes. Stacking a 301 on top of a proxy that enforces the opposite direction is exactly how you get the redirect loop described under caching layers below. The rest of the URL rules are in blog URL structure.

Only the blog path changes

Every other path on your domain keeps going where it already goes. A correct rule matches /blog and /blog/* and nothing else. This is why the setup is additive rather than a migration: you are not moving your site, you are carving out one path. The rule only handles routing, though. The blog behind it has to emit subdirectory-correct output, meaning internal links, canonical tags, the XML sitemap, and JSON-LD schemas that all carry the /blog prefix, or the proxy works and the rankings still do not follow.

If you have not picked a blog platform yet, how to add a blog to an existing website compares the options before you start writing config.

Next.js: rewrites in next.config.js

Next.js rewrites can target an external URL, which makes this a framework-level change with no separate proxy service. Put the rules in beforeFiles so they are checked before the filesystem and before any dynamic route that might otherwise claim /blog.

// next.config.js
const nextConfig = {
  async rewrites() {
    return {
      beforeFiles: [
        {
          source: '/blog',
          destination: 'https://yourblog.superblog.click/',
        },
        {
          source: '/blog/:path*',
          destination: 'https://yourblog.superblog.click/:path*',
        },
      ],
    };
  },
};

module.exports = nextConfig;

Two rules, because the bare /blog request should map explicitly to the origin root rather than relying on an empty wildcard expansion. Next.js's own external-rewrite example uses the same two-rule shape, and it is what Superblog's dashboard generates. :path* is a wildcard that matches nested segments, so /blog/a/b/c still resolves.

If your project sets trailingSlash: true, add the slash to both sides: source: '/blog/:path*/' and a destination ending in /. Redeploy, or restart the server if you self-host. Next.js specifics, including when a managed blog layer beats building your own MDX pipeline, are in the Next.js blog guide.

Starting point, not a drop-in: your origin hostname and blog path will differ.

Vercel: rewrites in vercel.json

For a Vercel project that is not Next.js (or where you would rather keep routing out of application code), the same mapping goes in vercel.json at the project root:

{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "rewrites": [
    {
      "source": "/blog",
      "destination": "https://yourblog.superblog.click/"
    },
    {
      "source": "/blog/:path*",
      "destination": "https://yourblog.superblog.click/:path*"
    }
  ]
}

Vercel rewrites accept an absolute external URL as the destination, which is what turns this into a proxy rather than an internal path remap. Commit and push, or run vercel deploy, and the routes take effect on the next deployment.

If you need finer control, for example forcing a trailing slash on post URLs while leaving static assets alone, vercel.json also supports a lower-level routes array with PCRE regular expressions and $1 capture groups. That is what Superblog's dashboard generates for Vercel sites, because it can treat /blog/image.png and /blog/post-name differently in a way that :path* cannot. Reach for it only when the higher-level rewrites shape is not enough.

Nginx: a location block with proxy_pass

On your own server, the whole thing is one block inside your existing server { }:

# Reverse proxy /blog to the blog origin
location /blog/ {
    rewrite ^([^.]*[^/])$ $1/ permanent;
    proxy_ssl_server_name on;
    proxy_ssl_verify off;
    proxy_set_header Host "yourblog.superblog.click";
    proxy_set_header X-Forwarded-Host "";
    proxy_set_header X-Forwarded-For "";
    proxy_set_header Accept-Encoding "";
    proxy_set_header Cookie "";
    proxy_pass https://yourblog.superblog.click/;
    proxy_redirect ~^(http://[^/]+)(/.*)$ https://$http_host$2;
}

Line by line, because each one is load-bearing:

  • The trailing slash on proxy_pass https://.../ is what strips the prefix. When proxy_pass includes a URI, Nginx replaces the matched location prefix with it. Drop that final slash and the full /blog/post-name/ path gets forwarded upstream, which is the double-prefix failure.
  • proxy_set_header Host pins the tenant the shared origin resolves. Nginx defaults to $proxy_host, but a config that sets Host $host in this location sends yoursite.com upstream and the origin 404s because it cannot match a blog to that name. Set it explicitly so the behavior does not depend on what the rest of your config does.
  • The blank X-Forwarded-Host and X-Forwarded-For values stop your own hostname and the visitor IP from reaching the origin and confusing tenant resolution. They are here because proxy_set_header stops inheriting from the server level as soon as any proxy_set_header appears at the location level, so a block you copy has to carry every header it needs.
  • proxy_ssl_server_name on sends SNI on the upstream TLS handshake, which a multi-tenant HTTPS origin needs to present the right certificate.
  • The rewrite line canonicalizes to the trailing-slash form. Flip it to rewrite ^(/blog/.+)/$ $1 permanent; if your blog publishes URLs without the trailing slash.
  • proxy_redirect rewrites upstream redirect targets back onto your domain over HTTPS, so a redirect from the origin does not leak the blog host into the address bar.

Test and reload with nginx -t && nginx -s reload. Apache and IIS follow the same shape with ProxyPass / ProxyPassReverse and URL Rewrite rules respectively.

Cloudflare Workers: a route in front of everything

The advantage of the Worker approach is that it does not care what your main site is built with. Cloudflare sits in front of the request before it ever reaches your origin, so this works for hosted builders that cannot run server code themselves.

const BLOG_ORIGIN = 'https://yourblog.superblog.click';
const PREFIX = '/blog';

export default {
  async fetch(request) {
    const url = new URL(request.url);
    const isBlog = url.pathname === PREFIX || url.pathname.startsWith(`${PREFIX}/`);

    if (!isBlog) {
      return fetch(request);
    }

    const target = new URL(url.pathname.slice(PREFIX.length) || '/', BLOG_ORIGIN);
    target.search = url.search;

    return fetch(new Request(target.toString(), request));
  },
};

Then attach the Worker to a route of yoursite.com/blog* in your zone, and set SSL/TLS encryption mode to Full so Cloudflare talks HTTPS to the origin. Requests to every other path fall through to fetch(request) untouched. (The yoursite.com/blog* route pattern also matches paths like /blogging-tips. That is harmless here, because the Worker's own isBlog check fails on them and they fall through to your origin unchanged.)

One Cloudflare-specific trap: when you first move a domain onto Cloudflare, add the DNS records for your main site with the orange-cloud proxy toggle off, complete verification in your host's dashboard, and only then turn the proxy on. Verification against a proxied record fails on several platforms.

Netlify: a proxy redirect with status 200

Netlify calls a proxy a redirect with a 200 status. Put it in netlify.toml, above any existing redirect rules:

[[redirects]]
  from = "/blog"
  to = "https://yourblog.superblog.click/"
  status = 200
  force = true

[[redirects]]
  from = "/blog/*"
  to = "https://yourblog.superblog.click/:splat"
  status = 200
  force = true

:splat carries whatever the * matched. status = 200 is what makes it a proxy rather than a redirect, so the visitor's URL stays on your domain. force = true makes the rule win over any matching static file in your build output.

The _redirects file form is the same rule on one line, where ! is the force flag:

/blog/*  https://yourblog.superblog.click/:splat  200!

Use one or the other. Rules in _redirects are processed ahead of netlify.toml, so keeping both around is how you end up debugging a rule that is being silently overridden.

The no-config path: let the platform run the proxy

If you have no place to put a routing rule, because your main site sits on a builder with no server-side config and you would rather not add Cloudflare, the proxy can be run for you.

Superblog supports this arrangement. The shape of it: your main site moves to proxy.yoursite.com, the apex domain's DNS points at Superblog's edge instead, and Superblog serves /blog from its own infrastructure while forwarding every other path to your site. Visitors never see the proxy subdomain. Setting it up starts with an email to hello@superblog.ai naming the blog path you want, since the routing is configured on their side rather than yours. Worth planning around: the first DNS step disconnects the apex domain from your current host, so the site is briefly offline while it sits unattached.

Two things to know before choosing it. Your main site's pages need canonical tags pointing at the apex domain, because they are now being served through a proxy, and proxy.yoursite.com should carry a Disallow: / robots.txt so Google does not index the intermediary alongside the real thing. That is more DNS surgery than a rewrite rule in a config file, so it is the fallback rather than the default. If any of the five configs above are available to you, use them.

Common pitfalls

Asset paths that resolve outside /blog

If the blog platform emits root-relative asset URLs like /assets/style.css, those requests land on your main site once the blog is served from a path, and you get an unstyled page. A platform built for subdirectory serving emits paths already prefixed with your blog path, so nothing needs rewriting. If yours does not, you are stuck rewriting HTML in the proxy layer, which is slow and fragile. Test this before you commit to a platform.

Canonical tags still pointing at the blog host

This is the failure that costs rankings. If pages served at yoursite.com/blog/post-name/ carry <link rel="canonical" href="https://yourblog.example-host.com/post-name/">, you have told Google the real page is somewhere else, and the subdirectory URLs will not be the ones that rank. Curl any post and check the canonical resolves to your domain and your blog path.

robots.txt lives at the root, always

A robots.txt at /blog/robots.txt is ignored by every crawler. The only file that counts is yoursite.com/robots.txt, served by your main site, and it needs to allow the blog path and reference the blog's sitemap. See robots.txt for blogs.

Caching layers and redirect loops

If your CDN or Nginx config caches static assets aggressively, exclude the blog path or you will serve stale assets after the blog redeploys. And if both your proxy and the blog origin enforce trailing slashes in opposite directions, you get an infinite redirect. Check for that with curl -IL on a post URL and count the hops: one 200, no chain.

How Superblog handles the blog side

The proxy is only half the job. The other half is a blog that is built to be served from a path on someone else's domain, which most platforms are not.

Superblog pre-renders every page as static HTML on its own CDN, with the subdirectory path baked into the output. Internal links, asset URLs, canonical tags, XML sitemaps, and JSON-LD schemas all resolve at yoursite.com/blog/... rather than the origin hostname, so the proxy stays a dumb path mapping instead of an HTML rewriter. llms.txt follows the same rule, landing at yoursite.com/blog/llms.txt for subdirectory installs.

The dashboard generates the exact config for your stack rather than making you adapt a generic template. Supported setups include Next.js, Vercel, Netlify, Nginx, Apache, IIS, Cloudflare, AWS Amplify, AWS CloudFront, Render, Shopify, Webflow, Framer, Squarespace, Wix, WordPress, Bubble, and Carrd, with the trailing-slash variant matched to what your blog publishes. Every plan carries subdirectory hosting, and no particular CDN vendor has to sit in front of your site for the routing to work.

Pro runs $49/month and Super runs $99/month. Both open with a 7-day trial that never asks for a card. See subdirectory hosting for what the feature covers, custom domain setup for the connection flow, or Superblog for Next.js if that is your stack.

Testing checklist

Run these against production after the rule goes live.

  1. curl -I https://yoursite.com/blog/ returns 200, not 301, 404, or 502.
  2. A post URL returns 200 and its HTML contains a canonical tag pointing at https://yoursite.com/blog/post-name/.
  3. curl -IL on a post shows no redirect chain and no loop.
  4. The blog's CSS and images load from paths under your domain, checked in devtools with the network panel, not just by eye.
  5. https://yoursite.com/ and three or four other main-site pages still work. Especially anything with a path that starts with the same letters as your blog path.
  6. https://yoursite.com/robots.txt exists, allows the blog path, and lists the blog sitemap.
  7. The blog sitemap lists URLs on your domain, not the origin hostname.
  8. Fetch a post with Google Search Console's URL Inspection tool and confirm the rendered HTML is the blog page.
  9. Publish a test post and confirm it appears at the new path within a few minutes, which verifies the proxy is not caching a stale response.

Getting it live

The routing rule is fifteen lines of config. The parts that actually decide whether a subdirectory blog earns rankings are the ones underneath it: canonicals that point at your domain, asset paths that resolve under your blog path, and a platform that regenerates sitemaps and schemas at the subdirectory rather than at its own hostname.

Pick the config above that matches your stack, run the checklist, and you are done in an afternoon. If you would rather the blog side arrive already correct, start a Superblog trial and connect your domain.

FAQ

How do I host a blog in a subdirectory?

Add a reverse proxy rule to whatever serves your main domain, mapping /blog and /blog/* to your blog platform's origin and stripping the /blog prefix before the upstream fetch. On Next.js that is a rewrite in next.config.js, on Vercel a rewrite in vercel.json, on Netlify a [[redirects]] block with status = 200, on Nginx a location block with proxy_pass, and on Cloudflare a Worker attached to a yoursite.com/blog* route. Then verify the blog's canonical tags point at your domain.

Is a subdirectory blog better for SEO?

For a business blog whose job is organic growth, yes, though not because Google ranks the URL format differently. Google has said it treats subdomains and subdirectories the same. What changes is that internal links between the blog and your product pages become same-domain links, the blog inherits the root domain's history, and the whole site is one crawl surface. The full argument, including when a subdomain is the right call, is in subdomain vs subdirectory.

Do I need Cloudflare to run a subdirectory blog?

No. Cloudflare Workers is one option among five, and it matters mainly when your main site runs on a hosted builder that cannot execute server-side routing rules. If you are on Next.js, Vercel, Netlify, Nginx, or Apache, the rule belongs in your own config and Cloudflare is not involved.

Will the reverse proxy slow down my blog?

It adds one network hop between your edge and the blog origin, which when both sit behind a CDN is one hop between two CDN edges. What causes real slowdowns is a proxy layer doing HTML rewriting on every response to patch broken asset paths. Pre-rendered pages that already carry the correct subdirectory paths keep the proxy to a pass-through.

Where does robots.txt go for a subdirectory blog?

At the root of your domain, yoursite.com/robots.txt, served by your main site. Crawlers do not read a robots.txt at /blog/robots.txt. Make sure the root file does not disallow your blog path and that it references the blog's sitemap URL. Details in robots.txt for blogs.

Can I use a path other than /blog?

Yes. Every config here works with /resources, /guides, /learn, or anything else. Substitute your path in both the source and the route pattern. Pick it before you publish, because changing it later means a redirect for every post you have written.

What if my main site is on Webflow, Wix, or Squarespace?

Those platforms do not run custom server code, so the proxy sits in front of them at the edge, usually as a Cloudflare Worker on a route. The Worker config in this guide is the same one you would use. Superblog's dashboard generates the platform-specific steps, including the DNS ordering that trips up first-time setups.

Want an SEO-focused and blazing fast blog?

Superblog let's you focus on writing content instead of optimizations.

Sai Krishna

Sai Krishna
Sai Krishna is the Founder and CEO of Superblog. Having built multiple products that scaled to tens of millions of users with only SEO and ASO, Sai Krishna is now building a blogging platform to help others grow organically.

superblog

Superblog is a blazing fast blogging platform for beautiful reading and writing experiences. Superblog takes care of SEO audits and site optimizations automatically.