树洞剪藏#剪藏#树洞剪藏

为 Notion 公开网页免费自定义域名

2025 年 6 月 23 日1 分钟
分享Twitter / XTelegram微博
为 Notion 公开网页免费自定义域名 封面

同步来源:树洞剪藏 源站剪藏 ID:570 原文地址:https://www.ppanda.com/posts/tech/notion%E8%87%AA%E5%AE%9A%E4%B9%89%E5%9F%9F%E5%90%8D/

打开原文 · 打开源站剪藏快照


为 Notion 公开网页免费自定义域名

黑夜给了我黑色的眼睛,我却用它戴黑框眼镜

🏠 首页 • 🗃 归档 • 📖 读书破万卷 • 🐼 关于

为 Notion 公开网页免费自定义域名

Posted on

2024-12-09

网络技术

cloudflare

| 共 2510 字,阅读约 6 分钟 ,浏览了 次

0

上次搞了个 微信读书笔记自动同步到 Notion ,现在我已经把读书笔记的内容都迁移到 Notion上了 微信读书笔记 。

如果直接使用 Notion,生成的公开地址就是 Notion 自己的服务器,比如: 微信读书笔记 https://zhuxiaojian.notion.site/80580de1183c45ecb8efc8b2eabee30a ,这样的地址显然不够美观,而且别人看到这个地址也不一定愿意点进去。

于是根据网上的方案,我也改造成了 https://weread.ppanda.com/ 的方式。

具体方案如下,原文来自于 免费为Notion绑定域名 ,这里只是搬运留存。

具体效果可以参考:https://book.malinkang.com/,这个方案是基于开源方案fruitionsite:https://github.com/stephenou/fruitionsite。但是代码已经3年没有更新了,很多功能已经生效。所以我复制了一份源码修改了一些bug。

域名注册

可以选择在腾讯云或者阿里云上或者其他平台上购买域名。

腾讯云购买地址:https://buy.cloud.tencent.com/domain/。

阿里云购买地址:https://wanwang.aliyun.com/

修改DNS信息

注册cloudflare: https://dash.cloudflare.com/ ,登录成功之后添加点击添加域。

输入我们购买的域名,然后点击继续。

进入下一个页面,选择免费计划。这里免费计划在最下面,如果屏幕小的可能会看不到。

然后点击继续,会看到我们需要配置的DNS服务器。

打开购买域名的网站,将你购买的域名的DNS修改为cloudflare的DNS服务器。

腾讯云修改DNS教程:https://cloud.tencent.com/document/product/302/5518#id.3Aserveraddress 阿里云修改DNS教程:https://help.aliyun.com/zh/dws/user-guide/change-dns-servers-for-a-domain-name#6a5c8962350ho

生成代码

打开网站:https://site.notionhub.app/

输入我们的域名和要绑定域名的Notion链接,请确保你的Notion已经发布到网络上。

如果你需要配置多个页面,比如https://malinkang.com/book,https://malinkang.com/podcast这种,点击 ADD A PRETTY LINK 添加对应的路径和NOTION链接即可。

最后可以点击 TOGGLE STYLE AND SCRIPT SETTINGS 来设置网站标题描述和字体。

全部设置完成点击复制代码。

配置Cloudflare Worker

打开Cloudflare:https://dash.cloudflare.com/,选中Workers和Pages,然后点击创建按钮

输入名字,然后点击部署

点击编辑代码

将我们上一步生成的代码,粘贴到箭头所指的地方,然后点击部署。部署完成后点返回按钮返回。

选中设置->域和路由,点击添加,输入我们的域名然后点击添加域即可。

这里贴一下我生成后的代码

/* CONFIGURATION STARTS HERE */

/* Step 1: enter your domain name like fruitionsite.com */ const MY_DOMAIN = 'weread.ppanda.com' ;

/*

  • Step 2: enter your URL slug to page ID mapping
  • The key on the left is the slug (without the slash)
  • The value on the right is the Notion page ID */ const SLUG_TO_PAGE = { '' : '80580de1183c45ecb8efc8b2eabee30a' , };

/* Step 3: enter your page title and description for SEO purposes */ const PAGE_TITLE = '朱小贱的读书笔记' ; const PAGE_DESCRIPTION = '朱小贱的读书笔记' ;

/* Step 4: enter a Google Font name, you can choose from https://fonts.google.com */ const GOOGLE_FONT = '' ;

/* Step 5: enter any custom scripts you'd like */ const CUSTOM_SCRIPT = 朱小贱的读书笔记 ;

/* CONFIGURATION ENDS HERE */

const PAGE_TO_SLUG = {}; const slugs = []; const pages = []; Object .keys(SLUG_TO_PAGE).forEach(slug => { const page = SLUG_TO_PAGE[slug]; slugs.push(slug); pages.push(page); PAGE_TO_SLUG[page] = slug; });

addEventListener( 'fetch' , event => { event.respondWith(fetchAndApply(event.request)); });

function generateSitemap() { let sitemap = '' ; slugs.forEach( (slug) => (sitemap += 'https://' + MY_DOMAIN + '/' + slug + '' ) ); sitemap += '' ; return sitemap; }

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); url.hostname = 'www.notion.so' ; if (url.pathname === '/robots.txt' ) { return new Response( 'Sitemap: https://' + MY_DOMAIN + '/sitemap.xml' ); } if (url.pathname === '/sitemap.xml' ) { let response = new Response(generateSitemap()); response.headers.set( 'content-type' , 'application/xml' ); return response; } let response; if (url.pathname.startsWith( '/app' ) && url.pathname.endsWith( 'js' )) { response = await fetch(url.toString()); let body = await response.text(); response = new Response(body.replace( /www.notion.so/g , MY_DOMAIN).replace( /notion.so/g , MY_DOMAIN), response); response.headers.set( 'Content-Type' , 'application/x-javascript' ); return response; } else if ((url.pathname.startsWith( '/api' ))) { // Forward API response = await fetch(url.toString(), { body : url.pathname.startsWith( '/api/v3/getPublicPageData' ) ? null : request.body, headers : { 'content-type' : 'application/json;charset=UTF-8' , 'user-agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36' }, method : 'POST' , }); response = new Response(response.body, response); response.headers.set( 'Access-Control-Allow-Origin' , '*' ); return response; } else if (url.pathname.endsWith( ".js" )){ response = await fetch(url.toString()); let body = await response.text(); response = new Response( body, response ); response.headers.set( "Content-Type" , "application/x-javascript" ); return response; } else if (slugs.indexOf(url.pathname.slice( 1 )) > - 1 ) { const pageId = SLUG_TO_PAGE[url.pathname.slice( 1 )]; return Response.redirect( 'https://' + MY_DOMAIN + '/' + pageId, 301 ); } else { response = await fetch(url.toString(), { body : request.body, headers : request.headers, method : request.method, }); response = new Response(response.body, response); response.headers. delete ( 'Content-Security-Policy' ); response.headers. delete ( 'X-Content-Security-Policy' ); }

return appendJavascript(response, SLUG_TO_PAGE); }

class MetaRewriter { element(element) { if (PAGE_TITLE !== '' ) { if (element.getAttribute( 'property' ) === 'og:title' || element.getAttribute( 'name' ) === 'twitter:title' ) { element.setAttribute( 'content' , PAGE_TITLE); } if (element.tagName === 'title' ) { element.setInnerContent(PAGE_TITLE); } } if (PAGE_DESCRIPTION !== '' ) { if (element.getAttribute( 'name' ) === 'description' || element.getAttribute( 'property' ) === 'og:description' || element.getAttribute( 'name' ) === 'twitter:description' ) { element.setAttribute( 'content' , PAGE_DESCRIPTION); } } if (element.getAttribute( 'property' ) === 'og:url' || element.getAttribute( 'name' ) === 'twitter:url' ) { element.setAttribute( 'content' , MY_DOMAIN); } if (element.getAttribute( 'name' ) === 'apple-itunes-app' ) { element.remove(); } } }

class HeadRewriter { element(element) { if (GOOGLE_FONT !== '' ) { element.append( `

html : true }); } element.append( ` div.notion-topbar > div > div:nth-child(3) { display: none !important; } div.notion-topbar > div > div:nth-child(4) { display: none !important; } div.notion-topbar > div > div:nth-child(5) { display: none !important; } div.notion-topbar > div > div:nth-child(6) { display: none !important; } div.notion-topbar > div > div:nth-child(7) { display: none !important; } div.notion-topbar-mobile > div:nth-child(3) { display: none !important; } div.notion-topbar-mobile > div:nth-child(4) { display: none !important; } div.notion-topbar > div > div:nth-child(1n).toggle-mode { display: block !important; } div.notion-topbar-mobile > div:nth-child(1n).toggle-mode { display: block !important; }

html : true }) } }

class BodyRewriter { constructor(SLUG_TO_PAGE) { this .SLUG_TO_PAGE = SLUG_TO_PAGE; } element(element) { element.append( `Powered by Fruition

html : true }); } }

async function appendJavascript(res, SLUG_TO_PAGE) { return new HTMLRewriter() .on( 'title' , new MetaRewriter()) .on( 'meta' , new MetaRewriter()) .on( 'head' , new HeadRewriter()) .on( 'body' , new BodyRewriter(SLUG_TO_PAGE)) .transform(res); }

原理是用当前域名替换了真实的 notion api 服务,效果还不错。

📝 评论

评论区加载中,请稍等

« Prev

Next »

相关页面

Mac mini 利用 ClashX Pro 开启网关模式

Cloudflare Workers 生成占位图服务

年度总结-词云版

为 Notion 公开网页免费自定义域名

域名注册

修改DNS信息

生成代码

配置Cloudflare Worker

• 探索世界的好奇心万岁 •

访问量 次 • 访客数 次

相关文章