Skip to content

Subdirectory Hosting (Reverse Proxy)

This guide covers the subdirectory hosting setup for GEO FAQ pages — serving them as a path on your main domain, e.g. https://www.yourcompany.com/faq/.

This is the strongest setup for SEO, but it requires a one-time configuration on your CDN or web server. If you're not sure which hosting setup to pick, start with the Hosting Overview — it compares all three options (Rose-hosted default, subdomain, subdirectory).

Why Subdirectory Hosting (Instead of Subdomain)

Setup Example SEO authority flows to Setup effort
Default (Rose-hosted) faq.userose.ai/yourcompany.com/... faq.userose.ai (Rose) None
Subdomain (CNAME) faq.yourcompany.com/... faq.yourcompany.com DNS CNAME (5 min)
Subdirectory (proxy) www.yourcompany.com/faq/... www.yourcompany.com (your main domain) Edge/CDN rule (15–60 min)

Search engines treat subdomains as separate sites. Backlinks, topical authority, and crawl budget that the FAQ content earns do not flow back to your main domain when it lives on a subdomain.

Serving the FAQ as a subdirectory of www.yourcompany.com means every link, citation, and ranking signal the FAQ accumulates directly strengthens your main domain's SEO.

Pick this option if:

  • Your main domain is your primary SEO asset.
  • You have access to your CDN, web server, or hosting platform configuration.
  • You're willing to do a one-time technical setup (or have a developer / agency who can).

If not, the subdomain option is still a strong choice and requires only a DNS change.

How It Works

Subdirectory mode uses a reverse proxy: when a visitor (or Googlebot) requests https://www.yourcompany.com/faq/..., your CDN transparently fetches the page from Rose and returns it, without changing the URL in the browser bar.

Visitor → https://www.yourcompany.com/faq/
            │
            ▼  (your CDN / web server)
        Reverse proxy rule:
        forward to https://faq.userose.ai/yourcompany.com/
            │
            ▼
        Rose returns the rendered FAQ HTML
            │
            ▼
        Your CDN returns it under www.yourcompany.com/faq/...
            │
            ▼
        Visitor sees www.yourcompany.com/faq/... in the URL bar

This is not a redirect (which would change the URL and send SEO juice elsewhere). It is a transparent proxy: the response body comes from Rose, but the URL stays on your domain.

What You Need to Do

1. Pick the Subdirectory Path

The conventional choice is /faq, but anything works as long as it has at most two path segments:

  • https://www.yourcompany.com/faq/ ✅
  • https://www.yourcompany.com/help/ ✅
  • https://www.yourcompany.com/fr/faq/ ✅ (one FAQ page per market, see below)
  • https://www.yourcompany.com/resources/help/faq/ ❌ (more than two segments)
  • https://www.yourcompany.com/ ❌ (cannot be the root — would conflict with your main site)

Make sure the path you pick is not already used by your main site.

2. Configure the Reverse Proxy

Whatever your stack, the proxy rule must do these five things. The recipes below implement them; if your host configures the proxy for you (see Managed hosting), send them this list.

# Requirement Value (for yourcompany.com serving at www.yourcompany.com/faq)
1 Upstream: a hostname, never an IP address https://faq.userose.ai
2 Host header and TLS SNI sent upstream faq.userose.ai
3 Path mapping: keep everything after /faq /faq/<path> → /yourcompany.com/<path>; /faq → /faq/
4 Request header on every proxied request (required) X-Rose-Public-Base: https://www.yourcompany.com/faq
5 Strip cookies from the upstream request (recommended) remove Cookie

Why each one matters:

  • 1–2. faq.userose.ai runs on Cloudflare. Its IP addresses are shared and can change, and Cloudflare routes requests by hostname, so a request sent to an IP or with your own Host header is refused. Your host sees that as a 502 Bad Gateway.
  • 3. Pointing every /faq/* URL at the single FAQ page breaks sitemap.xml and robots.txt, which also live under /faq/.
  • 4. Once Rose switches to your public base (step 3), direct visits to faq.userose.ai/yourcompany.com/... are 301-redirected to your URL. The header tells Rose the request comes from your proxy, so it serves the page instead of redirecting it back to you in a loop. Its value must match your public base exactly.

Rose needs nothing else on its side: no IP address, no allowlist entry and no Cloudflare setting. (The Cloudflare custom-hostname setup only applies to the subdomain option.)

Pick the recipe that matches your stack. In every recipe, replace:

  • yourcompany.com with the domain you registered in Rose (your tenant key).
  • www.yourcompany.com with the public host that should serve the FAQ.
  • /faq with your chosen path segment if different.

Cloudflare Worker

Best if Cloudflare already proxies your domain (orange cloud), or you can move DNS to Cloudflare.

wrangler.toml:

name = "rose-faq-proxy"
main = "src/index.ts"
compatibility_date = "2025-01-01"
routes = [
  { pattern = "www.yourcompany.com/faq", zone_name = "yourcompany.com" },
  { pattern = "www.yourcompany.com/faq/*", zone_name = "yourcompany.com" },
]

src/index.ts:

const UPSTREAM_HOST = "faq.userose.ai";
const TENANT = "yourcompany.com"; // your Rose tenant key
const PREFIX = "/faq";

export default {
  async fetch(req: Request): Promise<Response> {
    const url = new URL(req.url);
    let tail = url.pathname.slice(PREFIX.length);
    if (tail === "" || tail === "/") tail = "/frequently-asked-questions";
    const upstream = `https://${UPSTREAM_HOST}/${TENANT}${tail}${url.search}`;

    const headers = new Headers(req.headers);
    headers.set("Host", UPSTREAM_HOST);
    headers.set("X-Forwarded-Host", url.host);
    headers.set("X-Forwarded-Proto", "https");
    headers.set("X-Rose-Public-Base", `https://${url.host}${PREFIX}`);
    headers.delete("Cookie"); // don't forward first-party cookies upstream

    const res = await fetch(upstream, {
      method: req.method,
      headers,
      body: ["GET", "HEAD"].includes(req.method) ? null : req.body,
      redirect: "manual",
    });

    // Rewrite upstream redirects back into your URL space
    if (res.status >= 300 && res.status < 400) {
      const loc = res.headers.get("Location");
      if (loc) {
        const r = new URL(loc, upstream);
        if (r.host === UPSTREAM_HOST && r.pathname.startsWith(`/${TENANT}`)) {
          const out = new Headers(res.headers);
          out.set(
            "Location",
            `https://${url.host}${PREFIX}${r.pathname.slice(TENANT.length + 1)}${r.search}`,
          );
          return new Response(res.body, { status: res.status, headers: out });
        }
      }
    }
    return res;
  },
};

Deploy: wrangler deploy.

One page per market. When each market is its own Rose site (for example /fr and /en-gb next to your main site), each market gets its own page and its own Rose tenant key, shown in the backoffice under Config Studio → Content agent → Domain with that market selected. Add a route pair per market (www.yourcompany.com/fr/faq and www.yourcompany.com/fr/faq/*), and replace TENANT and PREFIX with a list matched longest path first:

const MARKETS: [string, string][] = [
  ["/en-gb/faq", "yourcompany-com-en-gb"], // [path, Rose tenant key]
  ["/fr/faq", "yourcompany-com-fr"],
  ["/faq", "yourcompany.com"],
];

// At the top of fetch(), instead of the TENANT and PREFIX constants:
const match = MARKETS.find(([p]) => url.pathname === p || url.pathname.startsWith(`${p}/`));
if (!match) return fetch(req);
const [PREFIX, TENANT] = match;

The rest of the Worker is unchanged: it already sends each market's own X-Rose-Public-Base.

Vercel / Next.js

Use a route handler, not rewrites() in next.config.js: a rewrite cannot add the X-Rose-Public-Base request header (headers() only sets response headers), so the FAQ would redirect in a loop after step 3.

app/faq/[[...slug]]/route.ts (App Router):

const UPSTREAM_HOST = "faq.userose.ai";
const TENANT = "yourcompany.com"; // your Rose tenant key
const PREFIX = "/faq";
const PUBLIC_BASE = "https://www.yourcompany.com/faq";

export const dynamic = "force-dynamic";

async function proxy(req: Request, slug?: string[]): Promise<Response> {
  const url = new URL(req.url);
  const tail = slug?.length ? slug.join("/") : "frequently-asked-questions";
  const upstream = `https://${UPSTREAM_HOST}/${TENANT}/${tail}${url.search}`;

  const headers = new Headers(req.headers);
  headers.set("Host", UPSTREAM_HOST);
  headers.set("X-Forwarded-Host", url.host);
  headers.set("X-Forwarded-Proto", "https");
  headers.set("X-Rose-Public-Base", PUBLIC_BASE);
  headers.delete("Cookie");

  const res = await fetch(upstream, { method: req.method, headers, redirect: "manual" });

  // Rewrite upstream redirects back into your /faq URL space
  const loc = res.status >= 300 && res.status < 400 ? res.headers.get("Location") : null;
  if (loc) {
    const r = new URL(loc, upstream);
    if (r.host === UPSTREAM_HOST && r.pathname.startsWith(`/${TENANT}`)) {
      const out = new Headers(res.headers);
      out.set("Location", `${PREFIX}${r.pathname.slice(TENANT.length + 1)}${r.search}`);
      return new Response(res.body, { status: res.status, headers: out });
    }
  }
  return new Response(res.body, { status: res.status, headers: res.headers });
}

export async function GET(req: Request, ctx: { params: Promise<{ slug?: string[] }> }) {
  return proxy(req, (await ctx.params).slug);
}
export const HEAD = GET;

Nginx

location = /faq {
  return 301 /faq/;
}

location /faq/ {
  rewrite ^/faq/(.*)$ /yourcompany.com/$1 break;

  proxy_pass https://faq.userose.ai;
  proxy_set_header Host faq.userose.ai;
  proxy_set_header X-Forwarded-Host $host;
  proxy_set_header X-Forwarded-Proto https;
  proxy_set_header X-Rose-Public-Base https://$host/faq;
  proxy_set_header Cookie "";

  proxy_ssl_server_name on;
  proxy_ssl_name faq.userose.ai;

  proxy_redirect https://faq.userose.ai/yourcompany.com/ /faq/;
}

Apache (mod_proxy + mod_rewrite)

SSLProxyEngine on
ProxyPreserveHost Off

RewriteEngine On
RewriteRule ^/faq$ /faq/ [R=301,L]
RewriteRule ^/faq/(.*)$ https://faq.userose.ai/yourcompany.com/$1 [P,L]

# Scoped to /faq/ so the rest of your site keeps its cookies
<Location "/faq/">
  RequestHeader unset Cookie
  RequestHeader set X-Forwarded-Host "%{HTTP_HOST}s"
  RequestHeader set X-Rose-Public-Base "https://www.yourcompany.com/faq"
</Location>

ProxyPassReverse /faq/ https://faq.userose.ai/yourcompany.com/

AWS CloudFront

  1. Create a custom origin pointing at faq.userose.ai (HTTPS only, origin path /yourcompany.com).
  2. Add cache behaviors for path patterns /faq and /faq/* using the new origin.
  3. Attach an origin request policy that forwards X-Forwarded-Host and X-Rose-Public-Base.
  4. Attach a CloudFront Function (viewer-request event) to strip /faq from the path:
function handler(event) {
  var req = event.request;
  var tail = req.uri.replace(/^\/faq/, '');
  req.uri = (tail === '' || tail === '/') ? '/frequently-asked-questions' : tail;
  req.headers['x-rose-public-base'] = { value: 'https://www.yourcompany.com/faq' };
  return req;
}

WordPress (.htaccess)

RewriteEngine On
RewriteRule ^faq$ /faq/ [R=301,L]
RewriteRule ^faq/(.*)$ https://faq.userose.ai/yourcompany.com/$1 [P,L]

# Only on /faq/ requests (THE_REQUEST is the original request line)
RequestHeader unset Cookie "expr=%{THE_REQUEST} =~ m#^[A-Z]+ /faq/#"
RequestHeader set X-Rose-Public-Base "https://www.yourcompany.com/faq" "expr=%{THE_REQUEST} =~ m#^[A-Z]+ /faq/#"

Requires mod_proxy, mod_proxy_http, mod_headers and SSLProxyEngine on in the server config. Keep the expr= conditions: an unconditional unset Cookie would log everyone out of WordPress. expr= needs Apache 2.4.10 or later. Managed WordPress hosts usually disable proxying from .htaccess. Some offer a reverse-proxy option instead (see Managed hosting); otherwise put Cloudflare in front of WordPress and use the Cloudflare Worker recipe.

Managed hosting with a reverse-proxy option (Kinsta)

Some managed hosts configure the reverse proxy for you on request. Kinsta, for example, sells a reverse-proxy add-on that their support team sets up. Send them the five requirements with your values, for example:

Please proxy www.yourcompany.com/faq to Rose:

  • Upstream: https://faq.userose.ai (hostname, not an IP address). Host header and SNI: faq.userose.ai.
  • /faq/<path> → https://faq.userose.ai/yourcompany.com/<path>, keeping the rest of the path; /faq → /faq/.
  • Add the request header X-Rose-Public-Base: https://www.yourcompany.com/faq.
  • Don't forward cookies upstream.

If the host asks for an IP address or an X-Real-IP value for Rose, there is none to give: faq.userose.ai has no fixed IP, and Rose doesn't need to know your visitors' IPs. Ask them to proxy to the hostname instead.

Hosted CMS (Webflow, Wix, Squarespace, Shopify, HubSpot CMS, Framer)

These platforms do not let you configure reverse proxies directly. The workaround is to put a CDN you control in front of the CMS:

  1. Move DNS for www.yourcompany.com to Cloudflare (proxied, orange cloud).
  2. Keep the CMS as your origin — Cloudflare forwards everything to it by default.
  3. Add the Cloudflare Worker recipe above on the /faq* route. Cloudflare intercepts those requests before they reach the CMS.
  4. The rest of your site (/, /about, /pricing) keeps flowing to the CMS as before.

Webflow-specific note: set Cloudflare SSL mode to Full (strict), and verify a few non-FAQ pages still render correctly from Webflow after the proxy switch.

3. Tell Rose Your Public Base URL

Do this only once https://www.yourcompany.com/faq/ shows your FAQ in a browser. If you do it earlier, the 301 below sends visitors to a proxy that doesn't work yet.

Then email your Rose contact:

"Please configure https://www.yourcompany.com/faq as our GEO FAQ public base."

Use the exact host your site serves (with or without www); it must match the X-Rose-Public-Base header. We will:

  1. Set geo.publishing.public_base to your URL.
  2. Re-render your FAQ pages with the new canonical URL baked into the HTML, <link rel="canonical">, OpenGraph tags, and JSON-LD structured data.
  3. Update your sitemap.xml and robots.txt to point at the new base.
  4. Configure faq.userose.ai/yourcompany.com/* to 301-redirect to your new URL, so existing links keep working and Google consolidates the index.

4. Verify It Works

URL Expected Response
https://www.yourcompany.com/faq/ 200 OK — your FAQ page renders, URL bar stays on your domain
https://www.yourcompany.com/faq/sitemap.xml 200 OK — XML sitemap with URLs under your domain
https://www.yourcompany.com/faq/robots.txt 200 OK — allows all crawlers, sitemap pointer under your domain
https://faq.userose.ai/yourcompany.com/frequently-asked-questions 301 Moved Permanently → https://www.yourcompany.com/faq/
View page source on the FAQ page <link rel="canonical"> points at https://www.yourcompany.com/faq/

If www.yourcompany.com is already a verified Search Console property, you don't need to do anything — Google will discover the new pages on its next crawl. Speed things up by submitting the sitemap manually:

https://www.yourcompany.com/faq/sitemap.xml

Bing / IndexNow (subdirectory mode only). Unlike the default and subdomain setups, Rose does not auto-submit to IndexNow (Bing / ChatGPT search) in subdirectory mode. IndexNow verifies ownership by fetching a key file at your domain root (https://www.yourcompany.com/{key}.txt), which your /faq/* proxy rule does not serve. Bing still discovers your pages via the sitemap, just more slowly. To submit instantly, add your domain in Bing Webmaster Tools and submit https://www.yourcompany.com/faq/sitemap.xml.

What Rose Handles for You

  • Generation and hosting of the FAQ HTML, JSON-LD, sitemap, and robots.txt.
  • Canonical URL, OpenGraph tags, JSON-LD @id / url — all rebuilt from your public base.
  • Google sitemap kept fresh on every pipeline run (IndexNow auto-submit is not available in subdirectory mode — see the Bing note above).
  • 301 redirects from the legacy faq.userose.ai/... URLs to your new ones.
  • Assets (CSS, JS, fonts) served from faq.userose.ai/_assets/... with permissive CORS, so your proxy rule only needs to cover /faq/* — not asset URLs.

What to Watch Out For

Cache behavior

Your CDN will cache the proxied responses. Rose returns Cache-Control headers tuned for the GEO content (typically s-maxage=3600, stale-while-revalidate=86400). Most CDNs honor these by default. If you have an aggressive override, make sure it doesn't serve stale content for longer than 24 hours.

When Rose publishes new FAQ content, the upstream Last-Modified and ETag change — your CDN will revalidate on the next request. If you want instant updates, ask Rose to wire a cache-purge webhook into your CDN.

Asset URLs

The FAQ HTML references CSS, JS, fonts and other assets via absolute URLs on faq.userose.ai/_assets/.... Your proxy rule only needs to cover the HTML/XML/TXT endpoints under /faq/*. You don't need a separate rule for assets, and you don't need to worry about CSP — Rose sets the appropriate headers.

First-party cookies

Don't forward your site's first-party cookies upstream — Rose doesn't use them, and they add noise to logs. The recipes above strip Cookie from the proxied request.

WAF / bot rules

If your WAF blocks faq.userose.ai's IPs from making outbound requests to your /faq/... URLs, IndexNow ping-backs may fail. This is rare but if you have an aggressive WAF, allow the Rose user-agent (RoseGeoBot/1.0) on the /faq/* path.

Old subdomain URLs

If you previously used faq.userose.ai/yourcompany.com/... or faq.yourcompany.com, the 301 redirects we configure transfer link equity for ~6 months. After that, search engines fully consolidate the index on your new subdirectory.

TLS certificate

Your existing www.yourcompany.com certificate covers the new /faq/ path — there is nothing to configure or renew. Rose's own certificate on faq.userose.ai only matters for the proxy hop between your CDN and Rose, and is always valid.

Troubleshooting

Symptom on www.yourcompany.com/faq/... Likely cause Fix
502 Bad Gateway The proxy calls an IP address, or sends your own Host header or SNI Proxy to the hostname faq.userose.ai with Host and SNI set to faq.userose.ai (requirements 1–2)
Redirect loop or "too many redirects" (after step 3) X-Rose-Public-Base is missing, or doesn't match the public base exactly (www vs no www, trailing slash) Set the header to the exact public base (requirement 4)
FAQ page works but sitemap.xml or robots.txt show the FAQ page or a 404 Every /faq/* URL points at the single FAQ page Keep the rest of the path (requirement 3)
403 Forbidden from curl while the page works in a browser Your host's bot protection blocks command-line tools Test in a browser

Reverting

If you want to stop serving on your domain and go back to a subdomain or to faq.userose.ai:

  1. Tell us — we'll clear geo.publishing.public_base and re-render with the previous canonical URL.
  2. Remove the proxy rule from your CDN / web server.
  3. (Optional) Submit a removal request in Google Search Console for www.yourcompany.com/faq/* to clear it from the index faster.

Existing visitors and links to www.yourcompany.com/faq/... will start returning 404 once the proxy rule is removed — plan a redirect from your CDN to the new location if you keep getting traffic there.

Comparing All Three Setups

Rose-hosted (default) Subdomain (CNAME) Subdirectory (proxy)
Example URL faq.userose.ai/yourcompany.com/... faq.yourcompany.com/... www.yourcompany.com/faq/...
Setup time 0 minutes 5 minutes (DNS) 15–60 minutes (CDN config)
Who does the work Rose You add a CNAME You configure your CDN / server
TLS certificate Rose Rose (auto-provisioned) Yours (existing)
SEO authority flows to faq.userose.ai faq.yourcompany.com www.yourcompany.com
Brand consistency Rose-branded URL Your brand Your brand, your main domain
Failure mode n/a DNS resolution Proxy / origin error
Best for Trial, low-touch Most production deployments SEO-maximizing deployments

Most clients land on the subdomain option. Pick subdirectory when your main domain has significant existing SEO authority that you want the GEO content to inherit and reinforce.