DEV Community

Agam
Agam

Posted on

Custom BeeHiiv Home Page with Cloudflare

Before I migrated from Ghost to BeeHiiv, I wanted to keep my newsletter's landing page, it got me around 1.5k subs from HN because it was unique - so I knew I need to keep it.

To achieve this I managed to use Cloudfalre, and here is how you can too.

Basically, we will create a subdomain for the landing page:

  • unzip.dev/ → join.unzip.dev - my new home page.
  • unzip.dev/* - my main website for the newsletter (on BeeHiiv).

This tutorial assumes you also want to use your root domain on BeeHiiv instead of www.yourdomain.com

Step 1 - Connect your domain to BeeHiiv.

  1. First I added my domain unzip.dev.
  2. After verifying, add it to the Web Domain section.

Step 2 - Open a Cloudflare account

  1. Register to Cloudflare move your registrar's DNS Nameservers to CloudFlare.

Step 3 - Deploy your new home page

I used vercel.com and next.js. For that I added a CNAME to vercel for join.unzip.dev.

Step 4 - Patching CLoudflare

We want to hijack the requests to the root path. For that I had to add the following Cloudflare rules:
Cloudflare rules

  1. For the archive page I had in Ghost (optional).
  2. For the posts, in Ghost there are at: unzip.dev/[slug] on BeeHiiv they are at unzip.dev/p/[slug].
  3. For the homepage I added a redirect from / to join.unzip.dev (on vercel).

Bonus www -> root

To redirect all www. to root you have to add the following record:

  • Add the following bulk redirects:

WWW cloudflare rules

  • AAAA for www with the value 100:: otherwise cloudflare will not run the bulk rules.

API for landing page

To be able to subscribe people via my external landing page, I've added a cloudflare worker as follows:


export default {
  async fetch(request, env, ctx) {

    // Allow everyone to post to here via CORS
    const corsHeaders = {
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Methods": "POST",
      "Access-Control-Allow-Headers": 'Content-Type'
    };

    // A hack to fix the OPTIONS response for CORS
    if (request.method === "OPTIONS") {
      // Handle CORS preflight requests
      return new Response(null, {
        headers: {...corsHeaders}
      })
    }


    const publicationId = 'pub_YOUR_PUBLICATION_ID';

    // Extract the subscription details from the request body
    const requestBody = await request.json();
    const { email } = requestBody;

      // Create the subscription
      const response = await fetch(`https://api.beehiiv.com/v2/publications/${publicationId}/subscriptions`, {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${env.BEEHIIV_KEY}`, // Replace with your actual bearer token
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          email,
          reactivate_existing: true,
          send_welcome_email: true, // Always send the welcome email
          utm_source: 'join.unzip.dev', 
          utm_medium: 'web',
          referring_site: 'join.unzip.dev', 
        }),
      });

      if (!response.ok) {
        throw new Error('Failed to create subscription');
      }

    return new Response("Subbe", {
        headers: {
          ...corsHeaders,
        },
      });
    }
};

Enter fullscreen mode Exit fullscreen mode

At this point every time someone visits / they will be 301ed to join.unzip.dev (seeing my amazing landing page). Otherwise, they will be redirected to BeeHiiv to see the posts themselves.

I hope this helps other's that want to customize their BeeHiiv landing page.

Top comments (0)