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.airuns 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 ownHostheader is refused. Your host sees that as a502 Bad Gateway. - 3. Pointing every
/faq/*URL at the single FAQ page breakssitemap.xmlandrobots.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.comwith the domain you registered in Rose (your tenant key).www.yourcompany.comwith the public host that should serve the FAQ./faqwith 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¶
- Create a custom origin pointing at
faq.userose.ai(HTTPS only, origin path/yourcompany.com). - Add cache behaviors for path patterns
/faqand/faq/*using the new origin. - Attach an origin request policy that forwards
X-Forwarded-HostandX-Rose-Public-Base. - Attach a CloudFront Function (viewer-request event) to strip
/faqfrom 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/faqto Rose:
- Upstream:
https://faq.userose.ai(hostname, not an IP address).Hostheader 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:
- Move DNS for
www.yourcompany.comto Cloudflare (proxied, orange cloud). - Keep the CMS as your origin — Cloudflare forwards everything to it by default.
- Add the Cloudflare Worker recipe above on the
/faq*route. Cloudflare intercepts those requests before they reach the CMS. - 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/faqas 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:
- Set
geo.publishing.public_baseto your URL. - Re-render your FAQ pages with the new canonical URL baked into the HTML,
<link rel="canonical">, OpenGraph tags, and JSON-LD structured data. - Update your
sitemap.xmlandrobots.txtto point at the new base. - 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/ |
5. Add to Google Search Console (Recommended)¶
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:
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:
- Tell us — we'll clear
geo.publishing.public_baseand re-render with the previous canonical URL. - Remove the proxy rule from your CDN / web server.
- (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.