> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rankai.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Landing page connection guide

> Connect RankAI landing pages to your domain with Cloudflare (recommended for CMS sites), app rewrites, or a folder reverse proxy.

RankAI builds each landing page. This guide shows how that page gets a URL on your website.

Pick one setup before the first page goes live, and use that same setup for every landing page on the domain.

<Info>
  **In RankAI:** Open your project at `rankai.ai/dashboard/<project>/…`. The value after `/dashboard/` is your **project ID**. Each landing page has a **slug** you set when you create or edit the page.
</Info>

## What RankAI publishes

* Every landing page has a **slug** (for example `virtual-try-on`).

* RankAI stores the HTML on a CDN at:

  `https://cdn.rankai.ai/landing-pages/{projectId}/{slug}.html`

* Slugs are lowercase path segments. They may use letters, numbers, `/`, `_`, and `-`. Do not use a first path segment of `ai-fix` (reserved for other files on the CDN).

* Use the **slug** in your setup. Do not rely on internal page IDs.

## Compare setup options

| | A. Cloudflare (recommended) | B. Rewrites in your app | C. Reverse proxy under a folder |
| - | - | - | - |
| Best when | Webflow, Shopify, Framer, or another host that can sit behind Cloudflare | Next.js, Nuxt, or a similar app; only a few pages | You run Nginx or Apache and cannot put the domain on Cloudflare |
| Example URL | `https://www.example.com/virtual-try-on` | `https://www.example.com/virtual-try-on` | `https://www.example.com/lp/virtual-try-on` |
| After first setup | Publish in RankAI; routes update automatically | Each new page needs a code change and a deploy | New pages under the folder need no new rule |
| Who owns day-to-day | You own Cloudflare; RankAI can manage Worker routes | Your engineering team | Your engineering team |

## Prerequisites checklist

Before you start, confirm:

1. You know your RankAI **project ID** (from the dashboard URL).
2. You know the public hostname visitors use (`www`, the apex domain, or both).
3. You have picked option A, B, or C for this domain.
4. For option A: you can change nameservers or DNS so the hostname is **Proxied** on Cloudflare, and you can create a Worker in that Cloudflare account.
5. For option B or C: someone on your team can deploy app config or server config.

## Option A: Cloudflare Worker routes (CMS sites)

**Best for:** Webflow, Shopify, Framer, and other sites that already use Cloudflare, or that can move DNS to Cloudflare.

**Outcome:** Landing pages live at `https://www.example.com/{slug}` (or on the apex host). Other URLs still hit your CMS unchanged. When the project uses this architecture, RankAI creates, updates, and removes Worker **routes** as you publish, enable, or disable pages.

<Warning>
  Option A changes DNS, SSL, and Cloudflare Workers on your live marketing domain. Have someone technical do this setup. A wrong nameserver, DNS target, SSL mode, or Worker config can take the site down or cause redirect loops.
</Warning>

### 1. Put the domain on Cloudflare

1. Add the domain (zone) to Cloudflare and point the registrar nameservers to Cloudflare.
2. For the public hostname (usually `www` and/or the apex), set DNS to **Proxied** (orange cloud).
3. Keep the DNS **target** as your CMS host (for example Webflow’s CNAME), not an unrelated proxy hostname.

### 2. Set SSL/TLS to Automatic

This setting controls how Cloudflare talks to your CMS over HTTPS. Wrong mode is a common cause of redirect loops after you orange-cloud the hostname.

1. Sign in to the [Cloudflare dashboard](https://dash.cloudflare.com) and select the zone (domain) for this site.
2. In the left sidebar, open **SSL/TLS** → **Overview**.
3. Choose **Automatic SSL/TLS** (Cloudflare’s default for new zones). Cloudflare picks the strongest mode your origin supports—usually **Full** or **Full (strict)** for CMS hosts with a valid certificate.

If the zone uses **Custom SSL/TLS** instead, set the encryption mode to **Full** or **Full (strict)**—not **Flexible**.

**Flexible** encrypts only the visitor-to-Cloudflare hop and often breaks CMS hosts that already redirect HTTP to HTTPS.

<Warning>
  **Flexible** often causes redirect loops with Webflow, Shopify, and similar hosts. Prefer **Automatic SSL/TLS**, or **Full** / **Full (strict)** under Custom.
</Warning>

### 3. CMS prerequisites

<Warning>
  **Webflow:** Connect your custom domain in Webflow first and wait until SSL shows **Active**. DNS should still point at Webflow’s target (for example a CNAME to `cdn.webflow.com`) **while** orange-clouded (orange-to-orange). If Webflow never “owns” the hostname, Cloudflare can show error **1014** (CNAME Cross-User Banned).
</Warning>

* **Shopify:** Same orange-to-orange idea when Shopify documents custom domains behind Cloudflare. Cloudflare Workers may not run on `/checkout`. Be careful with **Always Use HTTPS** interactions after you change SSL mode.
* **Framer:** Use this only where Framer documents custom domain / proxy support (typically paid plans).
* **Unofficial builders:** Orange cloud can be flaky. Load the homepage several times before go-live.

### 4. Deploy the RankAI Worker once

In the **same Cloudflare account** as the zone:

1. Create a Worker named exactly `rankai-landing`.
2. Paste the script below and set `PROJECT_ID` to the RankAI project ID from your dashboard. One Worker per project is fine.
3. Deploy the Worker.

**What the Worker does:**

* Handles **GET** and **HEAD** only; other methods go straight to your origin.
* Normalizes the path to a slug (lowercase; spaces become `-`; only `a–z`, `0–9`, `/`, `_`, `-`). If the path does not already match those rules, it passes through.
* Skips reserved first path segments such as `ai-fix` (other files under the same CDN folder).
* Fetches `https://cdn.rankai.ai/landing-pages/{PROJECT_ID}/{slug}.html` with a 5-second timeout.
* If the CDN returns anything other than 200 (missing objects often return **403**, not 404), times out, or errors, it passes the request to your origin (`fetch(request)`).
* When it serves RankAI HTML, it sets `Content-Type: text/html; charset=utf-8`, keeps cache/`etag`/`last-modified` where present, and adds `x-rankai-worker: landing-page`.
* Uses `passThroughOnException()` so unexpected Worker failures still reach your site.

```javascript theme={null}
const CDN_ORIGIN = 'https://cdn.rankai.ai';
const CDN_TIMEOUT_MS = 5000;
const PROJECT_ID = ''; // paste your RankAI project ID

const RESERVED_FIRST_SEGMENTS = new Set(['ai-fix']);

function normalizeSlug(raw) {
  return raw
    .trim()
    .toLowerCase()
    .replace(/^\/+|\/+$/g, '')
    .replace(/\s+/g, '-')
    .replace(/[^a-z0-9/_-]/g, '')
    .replace(/\/+/g, '/');
}

function slugFromPath(pathname) {
  const requested = pathname.replace(/^\/+|\/+$/g, '');
  if (!requested) return null;

  const slug = normalizeSlug(requested);
  if (slug !== requested) return null;
  if (RESERVED_FIRST_SEGMENTS.has(slug.split('/')[0])) return null;
  return slug;
}

function landingPageResponse(cdnResponse, method) {
  const headers = new Headers();
  headers.set('Content-Type', 'text/html; charset=utf-8');
  headers.set('Cache-Control', cdnResponse.headers.get('cache-control') ?? 'public, max-age=60');
  headers.set('x-rankai-worker', 'landing-page');

  for (const name of ['etag', 'last-modified']) {
    const value = cdnResponse.headers.get(name);
    if (value) headers.set(name, value);
  }

  return new Response(method === 'HEAD' ? null : cdnResponse.body, { status: 200, headers });
}

export default {
  async fetch(request, env, ctx) {
    ctx.passThroughOnException();

    const method = request.method.toUpperCase();
    if (method !== 'GET' && method !== 'HEAD') return fetch(request);

    try {
      const url = new URL(request.url);
      const slug = slugFromPath(url.pathname);

      if (!slug || !PROJECT_ID) return fetch(request);

      const cdnUrl = `${CDN_ORIGIN}/landing-pages/${PROJECT_ID}/${slug}.html`;
      const cdnResponse = await fetch(cdnUrl, {
        headers: { Accept: 'text/html' },
        signal: AbortSignal.timeout(CDN_TIMEOUT_MS),
      });

      // S3 answers 403, not 404, for a missing object, so anything but 200 goes to the client's site.
      if (cdnResponse.status !== 200) {
        console.log(`[rankai] ${url.hostname}/${slug} -> cdn ${cdnResponse.status}, passthrough`);
        return fetch(request);
      }

      return landingPageResponse(cdnResponse, method);
    } catch (error) {
      console.log(
        `[rankai] error, passthrough: ${error instanceof Error ? error.message : String(error)}`
      );
      return fetch(request);
    }
  },
};
```

### 5. API token for RankAI automation

Create a Cloudflare API token so RankAI can manage Worker routes for this zone:

1. Create a Cloudflare API token scoped to **this zone** with:
   * **Zone → Zone → Read**
   * **Zone → Workers Routes → Edit**
2. Copy the **Zone ID** from the zone **Overview** sidebar.
3. Paste the Zone ID and token into RankAI.
4. Store the token somewhere safe. Cloudflare will not show the raw token again after you leave the create screen.

### 6. Routes RankAI creates

You usually do **not** create these by hand. After the Worker and API token are in place, RankAI adds a route automatically when you create or publish a landing page. Patterns look like:

* `example.com/{slug}*`
* `www.example.com/{slug}*`

The trailing `*` matters so query strings like `?utm=` still match.

<Warning>
  Use Worker **Routes**, not a Worker **Custom Domain**, when DNS for the hostname already exists. Attaching a Custom Domain on top of an existing CMS hostname can conflict with your DNS.
</Warning>

<Warning>
  When RankAI configures route failure behavior, it must be **Fail open (proceed)** so that if Workers hit limits, the main site still reaches your CMS.
</Warning>

### 7. After setup

| Action in RankAI | What happens on your domain |
| - | - |
| Publish or enable a page | The path becomes live (route added/updated) |
| Disable a page | The route is removed; CDN HTML is kept so re-enable is fast |
| Delete a page | The route is removed |

## Option B: Static list / rewrites in your app

**Best for:** Next.js, Nuxt, and similar apps when you only expect a handful of landing pages and your team owns deploys.

Your app keeps serving the rest of the site. For each landing page, you add an explicit rewrite or proxy: path on your site → RankAI CDN HTML.

**Pros:** No Cloudflare Worker. **Cons:** Not automatic for every new page—each new RankAI page needs a code change and a deploy (unless you build a more advanced dynamic check against the CDN).

### Find your project ID and slug

1. Project ID: the segment after `/dashboard/` in the RankAI URL.
2. Slug: the path segment you set on the landing page in RankAI (must match the CDN file name without `.html`).

### Next.js example

Add one rewrite per landing page in `next.config.js` (or `next.config.mjs`):

```javascript theme={null}
/** @type {import('next').NextConfig} */
const nextConfig = {
  async rewrites() {
    const projectId = 'YOUR_PROJECT_ID';
    const cdn = `https://cdn.rankai.ai/landing-pages/${projectId}`;

    return [
      {
        source: '/virtual-try-on',
        destination: `${cdn}/virtual-try-on.html`,
      },
      {
        source: '/summer-offer',
        destination: `${cdn}/summer-offer.html`,
      },
    ];
  },
};

module.exports = nextConfig;
```

Deploy after you add or remove a rewrite. Visitors stay on your domain; HTML loads from the CDN.

On another framework, do the same with that framework’s rewrite or a server route: one path on your site, proxied to `https://cdn.rankai.ai/landing-pages/YOUR_PROJECT_ID/your-slug.html`.

<Note>
  **Advanced:** Some teams add a dynamic fallback that checks the CDN for any unknown path. If the object is missing, the CDN may return **403**, not **404**—handle both when you design a fallback.
</Note>

## Option C: Reverse proxy under a folder

**Best for:** Nginx or Apache when you cannot put the marketing domain on Cloudflare orange cloud.

All RankAI pages live under a fixed prefix such as `/lp/`. One proxy rule covers every slug under that folder, so new pages do not need a new rule.

**Tradeoff:** URLs are not top-level paths on the marketing domain (unless you use option A or B).

Example:

* Works: `https://www.example.com/lp/virtual-try-on`
* Does not work with this option alone: `https://www.example.com/virtual-try-on`

Tell RankAI which folder you chose so generated links can use it.

Do **not** proxy the whole site (`/`). That would send every request to RankAI and take your existing site offline.

### Nginx

Put this in the server block for your domain. Replace `YOUR_PROJECT_ID`. Change `/lp/` if you pick another folder.

```nginx theme={null}
location /lp/ {
    rewrite ^/lp/(.*)$ /landing-pages/YOUR_PROJECT_ID/$1.html break;
    proxy_pass https://cdn.rankai.ai;
    proxy_ssl_server_name on;
    proxy_set_header Host cdn.rankai.ai;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
}
```

A request to `/lp/virtual-try-on` is fetched as `/landing-pages/YOUR_PROJECT_ID/virtual-try-on.html` on the CDN. Reload Nginx after you save the file.

### Apache

Enable `mod_proxy`, `mod_proxy_http`, `mod_rewrite`, and SSL proxying, then add:

```apacheconf theme={null}
SSLProxyEngine on
RewriteEngine on
RewriteRule ^/lp/(.*)$ https://cdn.rankai.ai/landing-pages/YOUR_PROJECT_ID/$1.html [P,L]
ProxyPassReverse /lp/ https://cdn.rankai.ai/landing-pages/YOUR_PROJECT_ID/
```

Restart Apache after the change.

## How to choose

* **Choose A** if you use a CMS (Webflow, Shopify, Framer, and similar) and can put the hostname on Cloudflare. This is the recommended self-serve path for those sites.
* **Choose B** if the list of pages is short, your framework can rewrite a path, and your team can deploy when that list changes.
* **Choose C** if DNS cannot move to Cloudflare orange cloud, you already terminate traffic on Nginx or Apache, and every landing page URL can include a folder such as `/lp/`.

## FAQ and troubleshooting

### Where do I find the project ID and slug?

Open the project in RankAI. The project ID is in the address bar after `/dashboard/`. The slug is the path you set on the landing page (for example `virtual-try-on` for `/virtual-try-on` or `/lp/virtual-try-on`).

### Cloudflare error 1014

The hostname is not authorized on the CMS Cloudflare SaaS side (common with Webflow). Fix the CMS custom domain connection and the DNS target first, then keep the record orange-clouded.

### Homepage intermittent timeouts on an obscure builder

Often an origin ↔ Cloudflare issue, not the RankAI landing Worker route. Test the homepage with Workers temporarily disabled on that hostname if you need to isolate the cause.

### Route not live yet

Wait briefly after RankAI creates a route. In Cloudflare, check **Workers** → **Routes** for the correct zone and pattern (must end with `*`).

### SSL redirect loops after setup

Confirm Cloudflare SSL/TLS is **Automatic**, or Custom set to **Full** / **Full (strict)**—not **Flexible**.

### I was told to attach a Worker Custom Domain

If DNS for the marketing hostname already points at your CMS, use **Routes** instead. Custom Domains are for hostnames that should be owned by the Worker.

### Can RankAI manage DNS for me instead?

Yes, as a RankAI-managed arrangement—not the default CMS self-serve path in this guide. [Contact support](/support/contact) if that is what you need.

***

*Last reviewed: October 15, 2026.*


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.