mirror of
https://github.com/mintlify/docs.git
synced 2026-09-14 13:35:46 +08:00
db88b6fd8c
Claude-Session: https://claude.ai/code/session_01NrNcqnQqnVxY8H1epWe88p
302 lines
12 KiB
Plaintext
302 lines
12 KiB
Plaintext
---
|
|
title: "Deploy at a subpath with Cloudflare Workers"
|
|
sidebarTitle: "Cloudflare"
|
|
description: "Deploy your Mintlify documentation at a subpath on your domain using Cloudflare Workers with step-by-step setup and DNS configuration."
|
|
keywords: ["Cloudflare Workers", "subpath routing", "reverse proxy setup", "Worker configuration", "Cloudflare WAF", "firewall rules", "Bot Fight Mode", "403 errors"]
|
|
boost: 3
|
|
---
|
|
|
|
import Propagating from "/snippets/custom-subpath-propagating.mdx";
|
|
import SubpathSetupSteps from "/snippets/subpath-setup-steps.mdx";
|
|
|
|
To host your documentation at a subpath such as `yoursite.com/docs` using Cloudflare, you must create and configure a Cloudflare Worker.
|
|
|
|
<Info>
|
|
Before you begin, you need a Cloudflare account and a domain name (managed on or off Cloudflare).
|
|
</Info>
|
|
|
|
## Set your base path
|
|
|
|
<SubpathSetupSteps />
|
|
|
|
The dashboard displays a Cloudflare Worker script with your subdomain, domain, and base path filled in. Use this script in the [Configure routing](#configure-routing) step instead of manually replacing the placeholder values in the example script.
|
|
|
|
## Set up a Worker
|
|
|
|
Create a Cloudflare Worker by following the [Cloudflare Workers getting started guide](https://developers.cloudflare.com/workers/get-started/dashboard/), if you have not already.
|
|
|
|
<Tip>
|
|
If your DNS provider is Cloudflare, disable proxying for the CNAME record to avoid potential configuration issues.
|
|
</Tip>
|
|
|
|
### Proxies with Vercel deployments
|
|
|
|
If you use Cloudflare as a proxy with Vercel deployments, you must ensure proper configuration to avoid conflicts with Vercel's domain verification and SSL certificate provisioning.
|
|
|
|
Improper proxy configuration can prevent Vercel from provisioning Let's Encrypt SSL certificates and cause domain verification failures.
|
|
|
|
#### Required path allowlist
|
|
|
|
Your Cloudflare Worker must allow traffic to these specific paths without blocking or redirecting:
|
|
|
|
- `/.well-known/acme-challenge/*`: Required for Let's Encrypt certificate verification.
|
|
- `/.well-known/vercel/*`: Required for Vercel domain verification.
|
|
|
|
While Cloudflare automatically handles many verification rules, creating additional custom rules may inadvertently block this critical traffic.
|
|
|
|
#### Header forwarding requirements
|
|
|
|
Ensure that your Worker sets the `Host` header to your `<subdomain>.mintlifysite.com` target, as shown in the example script, rather than passing through the original request's `Host` header. Incorrect `Host` headers cause verification requests to fail.
|
|
|
|
### Configure routing
|
|
|
|
In your Cloudflare dashboard, click **Edit Code** and add the script from your [Custom domain setup](https://app.mintlify.com/settings/deployment/custom-domain) page, which has your values filled in, or copy the following example script. See the [Cloudflare documentation](https://developers.cloudflare.com/workers-ai/get-started/dashboard/#development) for more information on editing a Worker.
|
|
|
|
<Tip>
|
|
If you use the example script, replace `[SUBDOMAIN]` with your unique subdomain, `[YOUR_DOMAIN]` with your website's base URL, and `/docs` with your desired subpath if different.
|
|
</Tip>
|
|
|
|
```javascript
|
|
addEventListener("fetch", (event) => {
|
|
event.respondWith(handleRequest(event.request));
|
|
});
|
|
|
|
async function handleRequest(request) {
|
|
try {
|
|
const urlObject = new URL(request.url);
|
|
|
|
// If the request is to a Vercel verification path, allow it to pass through
|
|
if (urlObject.pathname.startsWith('/.well-known/')) {
|
|
return await fetch(request);
|
|
}
|
|
|
|
// If the request is to the docs subpath or a Mintlify asset or API path
|
|
if (
|
|
/^\/docs/.test(urlObject.pathname) ||
|
|
/^\/mintlify-assets\//.test(urlObject.pathname) ||
|
|
/^\/_mintlify\//.test(urlObject.pathname)
|
|
) {
|
|
// Then Proxy to Mintlify
|
|
const DOCS_URL = "[SUBDOMAIN].mintlifysite.com";
|
|
const CUSTOM_URL = "[YOUR_DOMAIN]";
|
|
|
|
let url = new URL(request.url);
|
|
url.hostname = DOCS_URL;
|
|
|
|
let proxyRequest = new Request(url, request);
|
|
|
|
proxyRequest.headers.set("Host", DOCS_URL);
|
|
proxyRequest.headers.set("X-Forwarded-Host", CUSTOM_URL);
|
|
proxyRequest.headers.set("X-Forwarded-Proto", "https");
|
|
// If deploying to Vercel, preserve client IP
|
|
proxyRequest.headers.set("CF-Connecting-IP", request.headers.get("CF-Connecting-IP"));
|
|
|
|
return await fetch(proxyRequest);
|
|
}
|
|
} catch (error) {
|
|
// If no action found, serve the regular request
|
|
return await fetch(request);
|
|
}
|
|
}
|
|
```
|
|
|
|
<Warning>
|
|
In addition to your subpath, your Worker must proxy `/mintlify-assets/*`, which serves the CSS, JavaScript, and favicons for your documentation, and `/_mintlify/*`, which handles API playground requests.
|
|
|
|
If you route traffic to your Worker with route patterns instead of a custom domain, add routes for `yoursite.com/mintlify-assets/*` and `yoursite.com/_mintlify/*` alongside your subpath route. These paths must originate from the root of your domain, not your subpath.
|
|
</Warning>
|
|
|
|
Click **Deploy** and wait for the changes to propagate.
|
|
|
|
<Propagating />
|
|
|
|
### Test your Worker
|
|
|
|
After your code deploys, test your Worker to ensure it routes to your Mintlify docs.
|
|
|
|
1. Test using the Worker's preview URL: `your-worker.your-subdomain.workers.dev/docs`
|
|
2. Verify the Worker routes to your Mintlify docs and your website.
|
|
|
|
### Add a custom domain
|
|
|
|
1. In your [Cloudflare dashboard](https://dash.cloudflare.com/), navigate to your Worker.
|
|
2. Go to **Settings > Domains & Routes > Add > Custom Domain**.
|
|
3. Add your domain.
|
|
|
|
<Tip>
|
|
Add your domain both with and without `www.` prepended.
|
|
</Tip>
|
|
|
|
See [Add a custom domain](https://developers.cloudflare.com/workers/configuration/routing/custom-domains/#add-a-custom-domain) in the Cloudflare documentation for more information.
|
|
|
|
### Resolve DNS conflicts
|
|
|
|
If your domain already points to another service, you must remove the existing DNS record. Your Cloudflare Worker must control all traffic for your domain.
|
|
|
|
1. Delete the existing DNS record for your domain. See [Delete DNS records](https://developers.cloudflare.com/dns/manage-dns-records/how-to/create-dns-records/#delete-dns-records) in the Cloudflare documentation for more information.
|
|
2. Return to your Worker and add your custom domain.
|
|
|
|
## Webflow custom routing
|
|
|
|
If you use Webflow to host your main site and want to serve Mintlify docs at `/docs` on the same domain, configure custom routing through Cloudflare Workers. The Worker proxies all non-docs traffic to your main site.
|
|
|
|
<Warning>
|
|
Set up your main site on a landing page before deploying this Worker, or visitors to your main site may see errors.
|
|
</Warning>
|
|
|
|
1. In Webflow, set up a landing page for your main site like `landing.yoursite.com`. This is the page that visitors see when they visit your site.
|
|
2. Deploy your main site to the landing page. This ensures that your main site remains accessible while you configure the Worker.
|
|
3. To avoid conflicts, update any absolute URLs in your main site to be relative.
|
|
4. In Cloudflare, click **Edit Code** and add the following script into your Worker's code.
|
|
|
|
<Tip> Replace `[SUBDOMAIN]` with your unique subdomain, `[YOUR_DOMAIN]` with your website's base URL, `[LANDING_DOMAIN]` with your landing page URL, and `/docs` with your desired subpath if different. </Tip>
|
|
|
|
```javascript
|
|
addEventListener("fetch", (event) => {
|
|
event.respondWith(handleRequest(event.request));
|
|
});
|
|
async function handleRequest(request) {
|
|
try {
|
|
const urlObject = new URL(request.url);
|
|
|
|
// If the request is to a Vercel verification path, allow it to pass through
|
|
if (urlObject.pathname.startsWith('/.well-known/')) {
|
|
return await fetch(request);
|
|
}
|
|
|
|
// If the request is to the docs subpath or a Mintlify asset or API path
|
|
if (
|
|
/^\/docs/.test(urlObject.pathname) ||
|
|
/^\/mintlify-assets\//.test(urlObject.pathname) ||
|
|
/^\/_mintlify\//.test(urlObject.pathname)
|
|
) {
|
|
// Proxy to Mintlify
|
|
const DOCS_URL = "[SUBDOMAIN].mintlifysite.com";
|
|
const CUSTOM_URL = "[YOUR_DOMAIN]";
|
|
let url = new URL(request.url);
|
|
url.hostname = DOCS_URL;
|
|
let proxyRequest = new Request(url, request);
|
|
proxyRequest.headers.set("Host", DOCS_URL);
|
|
proxyRequest.headers.set("X-Forwarded-Host", CUSTOM_URL);
|
|
proxyRequest.headers.set("X-Forwarded-Proto", "https");
|
|
// If deploying to Vercel, preserve client IP
|
|
proxyRequest.headers.set("CF-Connecting-IP", request.headers.get("CF-Connecting-IP"));
|
|
return await fetch(proxyRequest);
|
|
}
|
|
// Route everything else to main site
|
|
const MAIN_SITE_URL = "[LANDING_DOMAIN]";
|
|
if (MAIN_SITE_URL && MAIN_SITE_URL !== "[LANDING_DOMAIN]") {
|
|
let mainSiteUrl = new URL(request.url);
|
|
mainSiteUrl.hostname = MAIN_SITE_URL;
|
|
return await fetch(mainSiteUrl, {
|
|
method: request.method,
|
|
headers: request.headers,
|
|
body: request.body
|
|
});
|
|
}
|
|
} catch (error) {
|
|
// If no action found, serve the regular request
|
|
return await fetch(request);
|
|
}
|
|
}
|
|
```
|
|
5. Click **Deploy** and wait for the changes to propagate.
|
|
|
|
<Propagating />
|
|
|
|
## Troubleshoot firewall blocking
|
|
|
|
If your documentation site shows 500 errors after a few seconds or experiences slow navigation, Cloudflare's firewall may be blocking requests to Mintlify assets.
|
|
|
|
### Symptoms
|
|
|
|
- Documentation page loads initially but crashes with a 500 error after 30-60 seconds.
|
|
- Slow or broken client-side navigation between pages.
|
|
- 403 errors in the browser console for requests to `/mintlify-assets/*` paths.
|
|
- Cloudflare security challenge messages about "malformed data" or "suspicious URL patterns."
|
|
|
|
### Root cause
|
|
|
|
Cloudflare's Web Application Firewall (WAF) and Bot Fight Mode can flag Mintlify asset requests as suspicious due to:
|
|
|
|
- Multiple `%` symbols in encoded URL parameters.
|
|
- Long query strings with special characters.
|
|
- Automated requests from idle tabs.
|
|
|
|
### Solution
|
|
|
|
Create a Cloudflare firewall rule to exempt Mintlify assets from security checks.
|
|
|
|
#### Create the firewall exception
|
|
|
|
1. Log in to your [Cloudflare dashboard](https://dash.cloudflare.com/).
|
|
2. Select your domain.
|
|
3. Navigate to **Security > WAF**.
|
|
4. Click **Create rule**.
|
|
5. Configure the rule with these settings:
|
|
|
|
**Rule name:** Allow Mintlify assets
|
|
|
|
**When incoming requests match:**
|
|
- Field: `Hostname`
|
|
- Operator: `equals`
|
|
- Value: `docs.yourdomain.com` (replace with your actual docs domain)
|
|
|
|
**And:**
|
|
- Field: `URI Path`
|
|
- Operator: `starts with`
|
|
- Value: `/mintlify-assets/`
|
|
|
|
**Then:**
|
|
- Action: `Skip`
|
|
- Select: `All remaining custom rules`, `Managed rules`, and `Super Bot Fight Mode`
|
|
|
|
6. Enable **Log** to track matched requests.
|
|
7. Click **Deploy**.
|
|
|
|
#### Verify the rule
|
|
|
|
After deploying:
|
|
|
|
1. Open your documentation site in a browser.
|
|
2. Leave the page idle for 2-3 minutes.
|
|
3. Navigate between pages.
|
|
4. Check the browser console for any 403 errors.
|
|
|
|
If issues persist, verify your rule configuration:
|
|
|
|
- Ensure the hostname exactly matches your docs domain.
|
|
- Confirm the URI path uses `starts with` (not `contains`).
|
|
- Do not include wildcards (`*`) in the path value.
|
|
- Verify that you enabled and deployed the rule.
|
|
|
|
### Common mistakes
|
|
|
|
- Using the `contains` operator with `/mintlify-assets/*`. The `*` is treated as a literal character, not a wildcard.
|
|
- Using `equals` for URI Path. This only matches the exact path `/mintlify-assets/` and not subpaths.
|
|
- Forgetting to skip Bot Fight Mode. Explicitly include it in the skip action.
|
|
- Setting the wrong hostname. It must match your actual documentation domain.
|
|
|
|
### Additional troubleshooting
|
|
|
|
If the firewall exception doesn't resolve the issue:
|
|
|
|
1. Check Cloudflare's **Security > Events** log for blocked requests.
|
|
2. Verify that your Cloudflare Worker (if using a custom subpath) sets the `Host` header to your `<subdomain>.mintlifysite.com` target instead of passing through the original request's `Host` header.
|
|
3. Temporarily set Security Level to "Essentially Off" to confirm that Cloudflare is the cause.
|
|
4. Review any custom Page Rules that might override the firewall exception.
|
|
|
|
### Example working configuration
|
|
|
|
```
|
|
Rule: Allow Mintlify assets
|
|
Status: Enabled
|
|
|
|
When incoming requests match:
|
|
(http.host eq "docs.yourdomain.com" and starts_with(http.request.uri.path, "/mintlify-assets/"))
|
|
|
|
Then:
|
|
Skip: All remaining custom rules, Managed rules, Super Bot Fight Mode
|
|
Log: Enabled
|
|
```
|