Connect paths with Cloudflare

Serve your help center, blog and changelog at example.com/help, example.com/blog and example.com/changelog, next to the site you already have.

Your domain already serves a website. You want the help center at example.com/help, the blog at example.com/blog and the changelog at example.com/changelog, without moving anything else. Cloudflare can forward just those paths to Relay: one small Worker does it for every workspace of a project, and each path is one route.

This takes about ten minutes. You need:

  • A domain on Cloudflare (its nameservers point to Cloudflare). Any plan works, including Free.
  • The project's Relay address, <project>.paralect.app. It's shown in Relay under Settings → Domain.
  • Workspaces whose paths are what you want the addresses to be.

1. Set the paths and the domain in Relay

Open the project's settings: hover the project in the sidebar and click the gear next to its name.

The gear next to the project's name in Relay's sidebar

Each workspace is served at its path, the first segment of its addresses. Pick the workspace under Workspaces in the settings and check Address → Path: help, blog, changelog, or whatever you want the addresses to read. Live at shows the address readers will use.

A workspace's Address settings: the path, and the address it's live at

Then go to Domain. Enter the domain the sites will be reached at, example.com, and save. Relay now writes links, share cards, canonical tags and sitemaps with that domain, so search engines and social networks see the addresses readers use. Under the domain, Sites lists every workspace at its address.

The project's Domain settings: the domain, the sites on it, and the Worker script filled in for the project

The page also shows the Worker script and the routes below, filled in for your project, so you can copy them from there.

2. Make sure the domain is proxied

In Cloudflare, open the zone and go to DNS → Records. The record for the host you're mounting on (example.com, or www, or whichever subdomain serves your site) must be Proxied, with the orange cloud on. Workers only run on proxied traffic.

If the host was DNS only before, switch it and check that your site still opens. Sites on Vercel, Netlify and most hosts work behind the proxy as they are; if you get a redirect loop or a certificate error, the zone's SSL mode is Flexible and your host insists on HTTPS. Rather than changing the whole zone, set it for this host alone: Rules → Configuration Rules → Create rule, match Hostname equals your host, and under the settings choose SSL/TLS → Full (strict).

3. Create the Worker

  1. In the Cloudflare dashboard, open Workers & Pages and click Create.
  2. Choose Start with Hello World and name the Worker, for example relay-sites. Click Deploy.
  3. Click Edit code, replace everything with the script below, and click Deploy.

Change RELAY to your project's address, https://<project>.paralect.app.

// Serves Relay sites at paths of this host (/help, /blog, …). Add a route
// to this Worker for each site's path, and one for /_relay/*.
const RELAY = "https://<project>.paralect.app";

export default {
  async fetch(request) {
    const url = new URL(request.url);
    const target = new URL(url.pathname + url.search, RELAY);
    const response = await fetch(new Request(target, request), {
      redirect: "manual",
    });
    // A redirect within Relay stays on this host.
    const location = response.headers.get("location");
    if (location?.startsWith(RELAY)) {
      const headers = new Headers(response.headers);
      headers.set("location", location.slice(RELAY.length) || "/");
      return new Response(response.body, {
        status: response.status,
        headers,
      });
    }
    return response;
  },
};

The Worker fetches the same path from Relay and returns the answer. The pages come back with links relative to their path, so /help/getting-started links to /help/another-article, and everything works under your domain. Redirects Relay sends (a site's root to its first page, say) are rewritten to stay on your host.

4. Add a route for each path

Open the Worker and go to Settings → Domains & Routes. Click Add, choose Route, pick the zone, and enter a route. Add one for each workspace, and one for the sites' scripts and styles. Relay's Domain settings list them for your project, each with a copy button:

The routes to add, listed in Relay's Domain settings with copy buttons

Typed out, for a domain with a help center, a blog and a changelog:

example.com/help*
example.com/blog*
example.com/changelog*
example.com/_relay/*

The * after a path matches the path itself and everything under it: /help, /help/, /help/getting-started, /help/sitemap.xml and /help/og/getting-started.png. It also matches /helpers, so pick paths that no page of your site starts with.

/_relay/* is where the sites' scripts and styles live. It's the same for every project, and the name is unusual enough not to collide with anything on your site.

5. Open the sites

Go to https://example.com/help. The help center should open, with your domain in the address bar and links staying under it. Check a blog post and a changelog entry too. Share one in Slack or on social media to see the card with the domain in its address.

Later, when you add a workspace to the project, add a route for its path. The Worker doesn't change.

When a page doesn't load

The page is your site's 404. The request never reached the Worker. Most often the DNS record is still DNS only: the response then has no cf-ray header and names your host (server: Vercel, say) instead of cloudflare. Switch the record to Proxied. Otherwise the route isn't matching: routes must be in the zone the host belongs to, and include the host, example.com/help*, not /help*.

A 404 page that is neither yours nor Relay's. Another rule in Cloudflare grabbed the path before the Worker, or after it on the way to Relay. Open https://<project>.paralect.app/<path> directly: Relay's answers carry an x-vercel-id header; if it's missing, a Worker route, Origin Rule, Redirect Rule or Page Rule matching that path is answering instead. Find it under the zone's Rules and Workers Routes.

"There's no page here." The request reached Relay, but no workspace of the project has that path. Compare the route with the workspace's path in its settings, including the case: paths are lowercase.

The page is unstyled, or opens and goes blank. The /_relay/* route is missing, so the scripts and styles are answered by your site instead of Relay.

Error 1000 or a redirect loop. The domain's DNS record points at Cloudflare itself, or your host and Cloudflare disagree about HTTPS. Set SSL/TLS to Full (strict) and make sure the record points at your host, not at a Cloudflare address.

Custom styles and scripts from the project don't apply. They run only when the request reaches Relay on the project's own address, which the Worker does. If you're opening the page on Relay's own host instead, they're left out on purpose.

Search engines

Each site has a sitemap at its path: https://example.com/help/sitemap.xml, https://example.com/blog/sitemap.xml. Your site's robots.txt is yours, so add a line for each:

Sitemap: https://example.com/help/sitemap.xml
Sitemap: https://example.com/blog/sitemap.xml
Sitemap: https://example.com/changelog/sitemap.xml

Every page carries a canonical tag with your domain, so the copies at <project>.paralect.app don't compete with it.

A domain of its own

For a domain that serves nothing but the sites, docs.example.com, skip the routes: on the Worker, go to Settings → Domains & Routes, click Add, choose Custom Domain and enter the domain. Cloudflare creates the DNS record and the certificate. Every path of the domain then goes to Relay, and its root opens the first workspace.

Two projects on one domain

A Worker forwards to one project. To mount workspaces of a second project on the same domain, create a second Worker with that project's RELAY address and add its routes there. Routes for the two Workers can't overlap, and /_relay/* belongs to one of them: both projects serve the same scripts and styles, so either works.