Setup Reverse Proxy

HelpGuides support configuration of reverse proxy for advanced scenarios.

What is Reverse Proxy?

For the sake of example, let's say you own a domain example.com. You have an existing website using that domain running WordPress where you host a blog: example.com/blog/example-name.

You then created a project on HelpGuides myproject.helpguides.io. And, you want to move your blog content from WordPress to HelpGuides while still using WordPress for your main website. This setup is known as a reverse proxy.

How it works

Requests sent to example.com/blog/example-name are proxied to myproject.helpguides.io/example-name. The blog content is hosted at myproject.helpguides.io, but the browser URL and canonical URL remains example.com/blog/example-name.

Why use a reverse proxy?

The use case for a reverse proxy is:

Setting up a Reverse Proxy

Setting up a Reverse Proxy is a complex topic. But platforms such as Cloudflare make these types of advanced configurations relatively simple.

Recommended

If you want to support a reverse proxy for your HelpGuides content, please contact support. Reverse proxy configuration is only available in our enterprise plans.

Reverse Proxy with Cloudflare

Important

Setting up a reverse proxy with Cloudflare requires mapping your DNS record to Cloudflare. If you cannot do this, you cannot use Cloudflare for reverse DNS.

Step 1 - Get your application domain

You Application Domain is found in your HelpGuides project settings:

When no reverse proxy is used, this is the path used for your documentation hosted on HelpGuides. Note, HelpGuides does support custom subdomains too.

Step 2 - Get your canonical path

Your canonical path is configured by the HelpGuides team. Once configured you can find it on the Advanced Tab in settings:

In the above example, we're showing how we map requests to httsp://helpguides.io/blog to our main domain https://vszh13.helpguides.io. This could just as easily be https://yourdomain.com/blog mapping to https://[your subdomain].helpguides.io.

Step 3 - Create a Cloudflare worker

Create a Cloudflare worker to manage reverse proxy requests from your domain into your helpguides.io project. You will need your Application Domain. The script below is used to rewrite requests that have a /blog/.

For example, a request to:

https://helpguides.io/blog/helpguidesio-now-supports-model-context-protocol-mcp

is reverse proxied to:

https://vszh13.helpguides.io/helpguidesio-now-supports-model-context-protocol-mcp

If you are going to use different paths, your configuration may be different.

export default { const app_domain "https://vszh13.helpguides.io/"; async fetch(request, env, ctx) { const incomingUrl = new URL(request.url); // Check if path starts with /blog or is exactly /blog if (incomingUrl.pathname === "/blog" || incomingUrl.pathname === "/blog/") { // Handle root blog page return fetchAndProcess(app_domain, request, incomingUrl); } // Handle all other blog paths like /blog/hello-world, /blog/site.json if (incomingUrl.pathname.startsWith("/blog/")) { const targetPath = incomingUrl.pathname.replace("/blog", ""); const targetUrl = `${app_domain}${targetPath}${incomingUrl.search}`; return fetchAndProcess(targetUrl, request, incomingUrl); } return new Response("Not Found", { status: 404 }); } } // Extracted handler function for reuse async function fetchAndProcess(targetUrl, originalRequest, incomingUrl) { const proxyRequest = new Request(targetUrl, { method: originalRequest.method, headers: new Headers({ ...Object.fromEntries(originalRequest.headers), 'X-Forwarded-Host': incomingUrl.hostname, // passes "root domain" }), body: originalRequest.body, redirect: "manual" }); const originResponse = await fetch(proxyRequest); const contentType = originResponse.headers.get("Content-Type") || ""; // HTML: rewrite relative paths if (contentType.includes("text/html")) { let html = await originResponse.text(); html = html.replace(/(href|src)=["']\/(?!\/)/g, `$1="${app_domain}`); return new Response(html, { status: originResponse.status, headers: { "Content-Type": "text/html", "Cache-Control": "public, max-age=60" } }); } // Other file types (CSS, JSON, etc.) return new Response(originResponse.body, { status: originResponse.status, headers: originResponse.headers }); }