How to Add a Custom Domain to Notion?

How to Add a Custom Domain to Notion?

May 15, 2023 08:38 PM
When you’re starting a business, it’s important to have an online presence that is easy to find and memorable to the people looking for you.
After creating your business plan and defining your brand, the first step towards this is setting up a website. Notion makes it easy to quickly set up an excellent website so that anyone who wants to learn more about you can do so with ease.
In today’s digital world, it’s essential to have your custom domain so people can find you and remember your business and or company name easily.
This article will show you step-by-step instructions on adding a custom domain to Notion. Note that this process only needs to be done once.
Here’s how to add a custom domain to Notion and get your brand in front of potential customers in no time.
notion image

What You Need

Before you dive head-first into adding your custom domain to Notion, there are a few prerequisites that you need to meet. These include:
  • A custom domain and full access to the DNS settings.
  • A landing page in Notion.
  • An account on Cloudflare.

Step 1

The first step to adding your custom domain to Notion is accessing the Notion page. It does not matter the page you choose to use; the only thing that matters is the page should be public. To do this, make sure the Public Access option is enabled.
To make everything easier, turn on the All-Search Engines should be enabled. Once all that is done, click on the Cope Page Link and save this for later use.

Step 2

Cloudflare is a popular CDN that gives users handy tools that deliver assets and protect your Notion page against attacks like DDoS. It also gives you more control and flexibility over your Notion website or app.
The most popular use is making the Workers feature easily forward incoming requests from the Users and the overall Notion public page created in step 1.
You need to register on the Cloudflare website to get started with Cloudflare. With that done, you can add your custom domain by clicking on the Add Site at the top right. If you are starting, then the free plan is the best choice to get your feet wet.
Once you select your plan, you need to scroll down. Here you will find a table that will require you to Log in to your registrar account and replace it with Cloudflare’s nameservers. For the login part, you can determine your registration via WHOIS. Once that is done, remove the nameservers and replace them with the Cloudflare nameservers.

Step 3

With the nameservers replaced, go to settings to set the default nameservers. These are found in the DNS settings of your domain provider. The only thing you need to do for this step is to paste the nameservers you copied in step 2.
notion image

Step 4

Until this step, everything has been step forward and direct. However, from here, you need to be a bit tech-savvy because you need to do some coding. However, this will not be from scratch; most will need you to copy and paste the code blocks.
When setting your custom domain, you will need the requests to be forwarded to your Notion page. For this to happen, you need a proxy; this can be obtained using a worker in Cloudflare.
To get this worker go to the Workers menu located at the top and select the Launch Editor feature. Once that is done, click the Add Script option and give it a name you can remember. On the left side of the Editor, enter this block of code.
const MY_DOMAIN = "" const START_PAGE = "" const DISQUS_SHORTNAME = "agodrich" addEventListener('fetch', event => { event.respondWith(fetchAndApply(event.request)) }) const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, HEAD, POST,PUT, OPTIONS", "Access-Control-Allow-Headers": "Content-Type", } function handleOptions(request) { if (request.headers.get("Origin") !== null && request.headers.get("Access-Control-Request-Method") !== null && request.headers.get("Access-Control-Request-Headers") !== null) { // Handle CORS pre-flight request. return new Response(null, { headers: corsHeaders }) } else { // Handle standard OPTIONS request. return new Response(null, { headers: { "Allow": "GET, HEAD, POST, PUT, OPTIONS", } }) } } async function fetchAndApply(request) { if (request.method === "OPTIONS") { return handleOptions(request) } let url = new URL(request.url) let response if (url.pathname.startsWith("/app") && url.pathname.endsWith("js")) { // skip validation in app.js response = await fetch(`${url.pathname}`) let body = await response. Text() try { response = new Response(body.replace(/, MY_DOMAIN).replace(/, MY_DOMAIN), response) response.headers.set('Content-Type', "application/x-javascript") console.log("get rewrite app.js") } catch (err) { console.log(err) } } else if ((url.pathname.startsWith("/api"))) { // Forward API response = await fetch(`${url.pathname}`, { body: request.body, // must match 'Content-Type' header headers: { 'content-type': 'application/json;charset=UTF-8', 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36' }, method: "POST", // *GET, POST, PUT, DELETE, etc. }) response = new Response(response.body, response) response.headers.set('Access-Control-Allow-Origin', "*") } else if (url.pathname === `/`) { // 301 redrict let pageUrlList = START_PAGE.split("/") let redrictUrl = `https://${MY_DOMAIN}/${pageUrlList[pageUrlList.length - 1]}` return Response.redirect(redrictUrl, 301) } else { response = await fetch(`${url.pathname}`, { body: request.body, // must match 'Content-Type' header headers: request.headers, method: request.method, // *GET, POST, PUT, DELETE, etc. }) response = new Response(response.body, response) if (DISQUS_SHORTNAME) { // Delete CSP to load disqus content response.headers.delete("Content-Security-Policy") // add disqus comment component for every notion page return new HTMLRewriter().on('body', new ElementHandler()).transform(response) } } return response } class ElementHandler { element(element) { // An incoming element, such as `div` element.append(` <script> let disqus = document.createElement("div") = "disqus_thread" = "100%" var disqus_config = function () { let pathList = window.location.pathname.split("-") let pageID = pathList[pathList.length - 1] = window.location.href; if (/^[\w]{32}$/.test(pageID)) { = pageID; }else{ = undefined; } }; (function () { var d = document, s = d.createElement('script'); s.src = 'https://${DISQUS_SHORTNAME}'; s.setAttribute('data-timestamp', +new Date()); (d.head || d.body).appendChild(s); })(); // if you want to hide some element, add the selector to hideEle Array const hideEle = [ "#notion-app > div > div.notion-cursor-listener > div > div:nth-child(1) > div.notion-topbar > div > div:nth-child(6)", "#notion-app > div > div.notion-cursor-listener > div > div:nth-child(1) > div.notion-topbar > div > div:nth-child(5)", "#notion-app > div > div.notion-cursor-listener > div > div:nth-child(1) > div.notion-topbar > div > div:nth-child(4)", ] // if you want to replace some element, add the selector and innerHTML to replaceEle Object const replaceEle = { "#notion-app > div > div.notion-cursor-listener > div > div:nth-child(1) > div.notion-topbar > div > div:nth-child(6)": "<span>agodrich<span>" } function hideElement(qs) { let eles = document.querySelectorAll(qs) eles && eles.forEach(ele => = "none") } function replaceElement(qs, _html) { let ele = document.querySelector(qs) if (ele) { ele.innerHTML = _html } } let MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; let body = document.querySelector('body'); let observer = new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { let pageContent = document.querySelector("#notion-app div.notion-page-content") if (pageContent) { if (pageContent.lastChild && !== "disqus_thread") { pageContent.append(disqus) DISQUS.reset({ reload: true }) console.log(+new Date()) } } hideEle.forEach( hideE => hideElement(hideE) ) Object.entries(replaceEle).forEach( item => { let [qs,_html] = item; replaceEle(qs,_html) }) }); }); observer.observe(body, { subtree: true, childList: true }); </script> <noscript>Please enable JavaScript to view the <a href="">comments powered by Disqus.</a></noscript> `, { html: Boolean }) console.log(`Incoming element: ${element.tagName}`) } comments(comment) { // An incoming comment } text(text) { // An incoming piece of text } }
Remember to change the first two lines to add your domain and public Notion Page. Once it is done, Save these changes. If you get a Missing Route error warning, ignore it.

Step 5

Step 5 is the last step for adding your custom domain to your Notion account. With this step, you tell the worker the routes or URL that the script should run. As you did in step 4, launch the Editor and click in the Add route.]Here you will set a route that the script should run. This route is Your_DOMAIN/*. Once set, select the script custom-domain-proxy that was saved in step 4.
Now you save the route and wait. You may have to wait for a few minutes or hours before this becomes active and your custom domain is added to your Notion account.

Or just Use Potion

Potion can allow you to use a custom domain without all the hard, technical setup. Potion recreates your Notion content and hosts your site. You can put it on a custom domain, add custom styling, change fonts and colors, add a navigation bar, and much more. If you really want the ease and flexibility of a Notion website but need more real website-building functionality, definitely try out Potion.
Built with