Most personal website tutorials start with a framework. This one starts with a page you have already written, and it shows you how to host a personal website for free without adding another CMS.
If your CV, project notes, publication list, or lab journal already lives in Notion, you do not need another CMS. You need a way to put that page on your own domain, make it indexable, and then stop thinking about it.
That is the narrow problem Nooxy solves. It is an open-source Notion reverse proxy that runs on Cloudflare Workers. You point it at your published Notion pages, and it serves them on your domain with clean URLs, canonical tags, a sitemap, and your own CSS and JavaScript.
Using Notion as a CMS keeps the writing workflow you already have. The free personal website hosting comes from Cloudflare Workers. Adding a Notion custom domain is optional. You can see the result on draphy.org, os.draphy.org, and em-ucd.com. One Nooxy user also built a fan-made Expedition 33 music wiki that documents 196 songs across four albums. It is available in English and Spanish.
This guide goes from an empty terminal to a live site. The deploy itself takes about 10 minutes. Hosting and TLS cost $0 on Cloudflare's free plan. The only optional cost is the domain name, usually $10 to $15 per year. You can start on a free *.workers.dev URL if you do not have one yet.
I am the maintainer of Nooxy, so this comparison is not neutral. I have tried to make it fair and specific, with the trade-offs marked clearly so you can decide if it fits. Nooxy is not affiliated with Notion or Cloudflare.
Last checked: October 2026. Nooxy v2.0.0.
The short version
- What you get: A personal website on your domain, with content managed in Notion.
- Stack: Notion for content, Nooxy for the proxy and SEO, Cloudflare Workers for hosting.
- Time: About 10 to 15 minutes if your domain is already on Cloudflare. First-time DNS can take longer.
- Cost: $0 for hosting, TLS, and Notion's free plan. A custom domain is roughly $10 to $15 per year. A
*.workers.devURL is free.- Coding: None for the setup. You copy commands and edit one config file.
- Best for: PhD students, researchers, writers, consultants, and developers who already write in Notion and want a low-maintenance site.
- Not a fit for: E-commerce, logged-in memberships, or a completely terminal-free setup. The comparison below shows the closest alternatives.
Jump to: The 10-minute setup · SEO checklist · Troubleshooting · The main trade-offs · FAQ
Why I built this
I wanted a personal site at draphy.org. I already documented everything in Notion, so paying another $20 a month for a CMS felt backwards.
I tried Fruition first. It was the free option most people recommended at the time, but its maintenance had slowed and I wanted something I could adjust myself. I tried a few smaller proxies too, but they were harder to verify.
So I wrote the small piece I actually needed: a proxy that sits between Notion and the browser, fixes the SEO plumbing, and lets me inject my own styles and scripts. No database. No build pipeline for content. No runtime dependencies. No monthly bill.
I have run Nooxy on my own sites since 2025. It is a focused open-source tool, maintained by me, with a test suite and a habit of fixing what breaks when Notion changes. That is the honest version.
What you actually need
| Requirement | Why you need it | Cost |
|---|---|---|
| Node.js 22 or newer | Runs the Nooxy CLI and Wrangler | Free |
| A Cloudflare account | Hosts the Worker and manages DNS | Free, no credit card for Workers Free |
| At least one published Notion page | Your content | Free plan is enough for a solo site |
| A domain name (optional) | A professional URL | Roughly $10 to $15 per year |
| 10 to 15 minutes | Setup | Your time |
Check your Node version first:
node -v
If it prints v22 or higher, you are ready. If not, install the current LTS from nodejs.org. Nooxy requires Node 22, so an old global Node install is the most common reason the CLI fails.
You do not need to know Git, React, or how Cloudflare Workers work. You do need to be comfortable pasting commands into a terminal. That is the one real barrier, and I will not pretend otherwise.
How Notion, Nooxy, and Cloudflare fit together
The architecture is intentionally boring.
Visitor -> yourdomain.com
|
v
Cloudflare Worker (Nooxy)
| fetch the published page
v
your-workspace.notion.site
|
v
HTML with your domain, SEO tags, CSS, and JS
Notion is the CMS. You write and edit there. Published changes show up on the site without a rebuild.
Nooxy is the translation layer. It maps a clean path like /about to a Notion page ID, fetches the public page, and rewrites the response. It removes Notion's noindex tag when indexing is enabled, adds a canonical URL, injects Open Graph and Twitter tags, adds a WebPage JSON-LD block with a nested WebSite, and serves /sitemap.xml and /robots.txt.
Cloudflare Workers is the host. The Worker runs on Cloudflare's edge network. The free plan includes 100,000 requests per day, which is far more than a personal site usually needs. You can check the current limits in Cloudflare's docs.
Cloudflare also has a cache that can sit in front of the Worker. It is off by default. Turn it on and a cached page is returned without running the Worker or fetching Notion. The setup below keeps it off; the Optional extras section shows the change if you want it.
There is no Notion API key, no integration token, and no database sync. Nooxy serves the published page itself. That also means it keeps working with Notion database views, filters, and live updates, because it is not converting your page to static HTML.
The trade-off is equally simple: this is a live proxy, not a static site generator. By default, every request fetches from Notion and rewrites the HTML, so the live page shares Notion's availability. That is what keeps edits live and databases interactive. If you need a site that always stays up independently of Notion, a static generator is the better fit.
What "free" actually means
A custom domain is not free, and I do not want anyone to start this guide thinking otherwise.
Here is the real cost picture for this stack, checked in October 2026:
| What | What it costs | Notes |
|---|---|---|
| Cloudflare Workers | $0 | 100,000 requests per day on the free plan |
| TLS certificate | $0 | Created and renewed by Cloudflare |
| Notion | $0 | The free plan is enough for a solo personal site |
| Nooxy | $0 | MIT licensed |
| Custom domain | Roughly $10 to $15 per year | Optional. The *.workers.dev URL is free |
That is the whole cost picture for this tutorial. The comparison near the end covers Notion Sites, Super.so, Fruition, and a custom static site, including what each one charges. Prices change, so check the current pages before you buy.
If $10 a year is still too much, launch on the free *.workers.dev URL first. You can add the domain later without rebuilding anything.
Cloudflare's Workers Cache has no separate price. A cache hit skips the Worker's CPU time, but it still counts as a request against the daily limit. The Optional extras section has the setup.
The 10-minute setup
You will do 11 steps. Steps 1 through 8 get you a live site on a workers.dev URL. Step 9 adds your domain. Steps 10 and 11 add pages and styling. The 10 minutes is hands-on time, not reading time, so read the steps once before you start.
If you get stuck, the full Cloudflare deployment guide has the same steps with more detail and troubleshooting. The Nooxy documentation links in this article are pinned to the commit this tutorial was written against, so they will not break when the README changes.
Step 1: Create the Cloudflare project
In an empty folder you want to work in, run:
npm create cloudflare@latest my-nooxy-site
You will be asked a few questions. Choose these options:
| Question | What to select |
|---|---|
| What would you like to start with? | Hello World example |
| Which template? | Hello World Worker |
| Which language? | TypeScript |
| Do you want to use git? | Your choice. Yes is fine |
| Do you want to deploy? | No, we will do it later |
The exact wording may change as Cloudflare updates its CLI. The important part is a plain Worker with TypeScript. Do not choose a static site or Pages template. Nooxy needs a Worker because it proxies requests.
Move into the project:
cd my-nooxy-site
At this point you should have src/index.ts, package.json, and wrangler.jsonc.
Step 2: Install Nooxy and create the config
Run these inside my-nooxy-site:
npm install nooxy
npx nooxy init
npx nooxy init creates a nooxy/ folder:
my-nooxy-site/
├── src/
│ └── index.ts
├── nooxy/
│ ├── config.js
│ ├── head.css
│ ├── head.js
│ ├── body.js
│ └── header.html
├── package.json
└── wrangler.jsonc
The four optional files are where your custom CSS, JavaScript, and header HTML go. You can leave them empty for now.
Step 3: Publish your Notion pages and copy the IDs
Every Notion page has a 32-character page ID. Nooxy uses that ID to connect a URL path to a page.
For each page you want on your site:
- Open the page in Notion.
- Click Share in the top right.
- Open the Publish tab.
- Turn on Publish to web or click Publish.
- Copy the public link.
The published link looks like this:
https://your-workspace.notion.site/Your-Page-Title-abc123def456789012345678901234ab
The page ID is the 32 hex characters at the end of the URL path, after the last hyphen:
abc123def456789012345678901234ab
Ignore anything after a ?, such as ?pvs=4. That is a tracking parameter, not part of the ID.
The hostname, your-workspace.notion.site, is your notionDomain. You will need both the ID and the hostname in the next step.
A few details that save time:
- The page ID contains only letters
athroughfand numbers0through9. - If your link shows a UUID with hyphens, remove the hyphens. Nooxy expects 32 characters with no separators.
- The page must be published as a Notion Site. The general "Anyone on the web with link" setting is not the same thing.
- The page ID is an identifier, not a password. It does not grant edit access, but it does point at a public page.
Check your subpages before you publish. Notion publishes a page's subpages by default. If you have private notes, drafts, or anything you would not put on the public internet under that page, restrict the subpage permissions first. Notion's own help page also notes that published page metadata can include contributor names, profile photos, and email addresses. Review the page before you make it public.
Notion explains the publishing flow in Publish a Notion Site.
Step 4: Configure the site
Open nooxy/config.js. Do not replace the whole file. Edit the matching values in place, and leave the imports and the generated custom* fields at the bottom alone.
The four required values look like this:
export const SITE_CONFIG = {
// Your site's hostname, without https://
// You can start with your workers.dev URL and update it after the first deploy.
domain: 'my-nooxy-site.YOUR-SUBDOMAIN.workers.dev',
// The hostname of your published Notion site
notionDomain: 'your-workspace.notion.site',
// Your name or site name
siteName: 'Your Name',
// URL path -> 32-character Notion page ID
slugToPage: {
'/': 'YOUR_HOME_PAGE_ID',
'/about': 'YOUR_ABOUT_PAGE_ID',
'/projects': 'YOUR_PROJECTS_PAGE_ID',
},
// Leave the rest of the file as it is.
};
Your actual file also has seo, pageMetadata, fof, subDomains, and other options. They are commented out by default. You can uncomment the ones you need later. The configuration reference lists every field.
For a basic site, add this seo block inside SITE_CONFIG:
seo: {
indexing: true,
defaultAuthor: 'Your Name',
brandReplacement: 'Your Name',
},
indexing: true is the default, but setting it explicitly makes the intent clear. It tells Nooxy to remove Notion's noindex tag and add canonical and robots tags. brandReplacement replaces "Notion" in meta tags with your name or brand.
If you do not know your workers.dev subdomain yet, leave the placeholder. Deploy once in Step 8, copy the real URL, update domain, and redeploy. The site works either way. Only the canonical URLs are wrong until you fix it.
One common mistake: domain is a hostname, not a full URL. Use example.com, not https://example.com.
Step 5: Generate the injected assets
Run:
npx nooxy generate
This reads head.css, head.js, body.js, and header.html, minifies them, and writes the generated strings that config.js imports. It creates nooxy/generated/.
Run this command every time you change anything in the nooxy/ folder. That is the rule that prevents the "why is my CSS not showing up?" problem later.
Step 6: Connect Nooxy to the Worker
Open src/index.ts and replace everything in it with this:
import { initializeNooxy } from 'nooxy';
import { SITE_CONFIG } from '../nooxy/config';
const proxy = initializeNooxy(SITE_CONFIG);
export default {
async fetch(request: Request): Promise<Response> {
return proxy(request);
},
};
Save the file. That is the entire integration. initializeNooxy returns a function that takes a standard Request and returns a standard Response.
Step 7: Test locally
Start the local Worker:
npm run dev
Open http://localhost:8787. You should see your Notion page, served by Nooxy.
Press Ctrl + C when you are done.
If it does not work, check these first:
| Problem | What to check |
|---|---|
| Page not found | Is the page published? Is the page ID exactly 32 characters? Does notionDomain match the published hostname? |
| Blank page | The page ID is usually wrong. Copy it again from the published URL |
| Cannot find module 'nooxy' | Run npm install nooxy in the project root |
| Port already in use | Stop the other process, or let Wrangler pick another port |
Nooxy automatically uses localhost for canonical URLs during local development, so you do not need to change domain just to test.
Step 8: Deploy
Log in to Cloudflare once:
npx wrangler login
This opens a browser window. Click Allow.
Then deploy:
npm run deploy
When it finishes, Wrangler prints a URL like this:
https://my-nooxy-site.your-subdomain.workers.dev
Open it. Your Notion page is now live on the internet.
If that URL does not match the domain in nooxy/config.js, update domain, then run:
npx nooxy generate
npm run deploy
You now have a working site. The rest is making it yours.
Step 9: Put it on your own domain
This is the part that makes the site feel real. It is also the only part that can cost money.
If you do not have a domain yet, buy one from Cloudflare Registrar, Namecheap, or Porkbun. A .com is usually in the $10 to $15 per year range. Prices vary by extension. Do not buy hosting. You do not need it.
If your domain is already on Cloudflare
- Go to Cloudflare Dashboard and open Workers & Pages.
- Select your Worker.
- Go to Settings > Domains & Routes.
- Click Add > Custom Domain.
- Enter your domain, for example
example.com, and confirm.
Cloudflare creates the DNS record and the TLS certificate for you. You do not need to add a CNAME manually. In fact, a Custom Domain cannot be created if the hostname already has a CNAME record, so delete any old record for that hostname first.
If your domain is not on Cloudflare yet
- In the Cloudflare dashboard, go to Websites > Add a Site.
- Enter your domain and choose the Free plan.
- Cloudflare gives you two nameservers.
- At your registrar, replace the current nameservers with Cloudflare's.
- Wait until the domain shows Active. This is usually under an hour, but it can take up to 24 hours.
- Then follow the Custom Domain steps above.
Update the config
Once the domain is attached, change domain:
domain: 'example.com',
Then run:
npx nooxy generate
npm run deploy
Make www work
example.com and www.example.com are different hostnames. Adding only one will not make the other work.
For SEO, pick one canonical version and redirect the other. The usual choice is www to apex:
- Add
www.example.comas a second Custom Domain on the same Worker. - Add this to
nooxy/config.js:
subDomains: {
www: {
redirect: 'https://example.com',
},
},
- Run
npx nooxy generateandnpm run deploy.
Now https://www.example.com returns a 301 redirect to https://example.com. All traffic and link equity point at one hostname.
If you prefer the opposite direction, reverse the values. Do not serve the same content on both hostnames without a redirect. That is duplicate content, and it splits whatever search authority you earn.
The Cloudflare custom domain docs confirm the important detail: Custom Domains require an exact hostname match, and Cloudflare creates the DNS record and certificate for you.
Step 10: Add more pages
To add a page:
- Create it in Notion.
- Publish it.
- Copy the 32-character page ID.
- Add a path and ID to
slugToPage:
slugToPage: {
'/': 'YOUR_HOME_PAGE_ID',
'/about': 'YOUR_ABOUT_PAGE_ID',
'/projects': 'YOUR_PROJECTS_PAGE_ID',
'/contact': 'YOUR_CONTACT_PAGE_ID',
'/docs/getting-started': 'YOUR_DOCS_PAGE_ID',
},
- Run:
npx nooxy generate
npm run deploy
Nested paths work. The sitemap is generated from this map, so any page you want search engines to discover should be in slugToPage.
Step 11: Make it yours
Nooxy gives you four injection points. They are all optional.
| File | What it does | When it runs |
|---|---|---|
nooxy/head.css |
Custom CSS | Injected into <head>
|
nooxy/head.js |
Early JavaScript | Before page content loads |
nooxy/body.js |
Main JavaScript | Before </body>
|
nooxy/header.html |
Custom header HTML | At the top of the page body |
A small CSS starting point:
/* nooxy/head.css */
.notion-topbar {
display: none !important;
}
.notion-page-content {
max-width: 900px;
margin: 0 auto;
}
A simple header:
<!-- nooxy/header.html -->
<nav class="site-nav">
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/projects">Projects</a>
<a href="/contact">Contact</a>
</nav>
After any change in nooxy/, run:
npx nooxy generate
npm run deploy
One detail from the docs: Nooxy injects its own stylesheet after yours, so a rule with the same specificity can lose even if it appears first. If you need to override Nooxy's own styles, use a more specific selector as well as !important. The project files documentation has examples.
Optional extras you may want later
You do not need any of these to launch. The table covers the four settings people ask about most often. All but the last live in nooxy/config.js:
| Setting | What it does |
|---|---|
fof: { page: 'YOUR_404_PAGE_ID', slug: '/404' } |
Sends unknown paths to a published Notion page instead of a plain 404 |
googleFont: 'Inter' |
Loads a Google Font site-wide |
nooxy: { showBadge: false } |
Hides the small "Made with Nooxy" badge, which is shown by default |
configKey |
Lets one Worker serve several sites, each with its own config |
One SEO caveat on fof: it redirects unknown paths with a 301, so the custom page returns a 200 rather than a true 404. That is friendlier for visitors, but if accurate status codes matter to you, leave fof off.
The configKey option is the only one that changes src/index.ts. Instead of passing the config directly, pass an object with a stable key:
const siteA = initializeNooxy({ configKey: 'site-a', config: SITE_A });
const siteB = initializeNooxy({ configKey: 'site-b', config: SITE_B });
Then choose the site by hostname in your fetch handler. The README section on hosting several sites has the complete example. If you omit configKey, Nooxy falls back to the site's domain, but an explicit key is clearer and is required when two sites share a domain.
As always, run npx nooxy generate and npm run deploy after changing the config.
Turn on Workers Cache
Every uncached request runs the Worker and fetches the Notion page. That is what keeps edits instant, but it is also the slowest part of a page load. Cloudflare's Workers Cache stores the finished HTML at the edge and returns it without running the Worker at all. It is the right cache for a Worker. A normal Cache Rule will not do this, because the rule sits with the zone cache, not in front of the Worker.
Two changes. First, turn it on in wrangler.jsonc:
"cache": {
"enabled": true
},
This needs Wrangler 4.69 or newer. The project you created in Step 1 already has a current Wrangler version.
Second, give the HTML an edge TTL. Notion sends cache-control: public, max-age=0, must-revalidate, and Nooxy passes that through. At max-age=0, Cloudflare will not serve a cached copy. Wrap the response in src/index.ts and set an edge-only header:
import { initializeNooxy } from 'nooxy';
import { SITE_CONFIG } from '../nooxy/config';
const proxy = initializeNooxy(SITE_CONFIG);
export default {
async fetch(request: Request): Promise<Response> {
const response = await proxy(request);
const headers = new Headers(response.headers);
const type = headers.get('content-type') || '';
if (response.status === 200 && type.includes('text/html')) {
headers.set('cloudflare-cdn-cache-control', 'public, max-age=300');
}
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers,
});
},
};
cloudflare-cdn-cache-control is read by Cloudflare and stripped before the response reaches the browser. The browser still revalidates on each visit, so visitors do not keep a stale page. The edge keeps the HTML for five minutes. Use 60 seconds if you edit often, or 3600 if you rarely change the page.
The status check matters. Without it, a 503 during a Notion outage would also be cached for five minutes, which turns a short outage into a longer one.
Deploy, then check it:
curl -I https://example.com/
curl -I https://example.com/
The second response should include cf-cache-status: HIT. The first is a miss while the cache fills.
Two caveats. Edits can take up to the TTL to appear. A redeploy starts a fresh cache because the Worker version is part of the cache key, so deploy when you need a change out immediately. And do not turn this on if you use configKey to serve several domains from one Worker: the cache key is the path, not the hostname, so two sites would share entries for the same path.
If you would rather keep every request live, leave the cache off. The site works fine without it.
The SEO checklist after launch
Nooxy handles the technical plumbing. The writing, links, and rankings are still yours to earn. Here is what to do after the site is live.
1. Check the three URLs
Open these in a browser:
https://example.com/robots.txt
https://example.com/sitemap.xml
https://example.com/
robots.txt should point to your sitemap. sitemap.xml should list every slug in slugToPage. The homepage should load.
2. Inspect the page source
Use View Page Source (Ctrl+U on Windows/Linux, Cmd+U on Mac), not the rendered DOM in DevTools. Nooxy rewrites the HTML on the server, so the source is what crawlers see.
Search for:
-
<link rel="canonical"and confirm it points to your domain, notnotion.site. -
<meta name="robots" content="index, followand confirm it is present. -
<title>and<meta name="description">. -
<script type="application/ld+json">and confirm the WebPage schema and its nested WebSite.
If the canonical URL points to notion.site, your domain value is wrong. Fix it and redeploy.
3. Add per-page metadata
This is the highest-impact SEO change you can make. Notion's default titles and descriptions are generic, and they often include the word "Notion".
Add a pageMetadata block to nooxy/config.js:
pageMetadata: {
'YOUR_HOME_PAGE_ID': {
title: 'Ada Lovelace | Mathematician and Writer',
description: 'Notes, papers, and projects by Ada Lovelace.',
image: 'https://example.com/og/home.jpg',
author: 'Ada Lovelace',
},
'YOUR_ABOUT_PAGE_ID': {
title: 'About Ada Lovelace',
description: 'A short biography, current work, and contact details.',
author: 'Ada Lovelace',
},
},
Use the page ID as the key. The image should be an absolute URL, ideally 1200 by 630 pixels. Add your own social image at that URL.
Then run npx nooxy generate and npm run deploy.
Nooxy also exposes seo.keywords and seo.aiAttribution. They are optional and do not replace a clear title, description, and page copy.
4. Submit the sitemap to search engines
For Google:
- Open Google Search Console.
- Add a property. A Domain property is easiest if your DNS is on Cloudflare.
- Verify ownership. Cloudflare makes the DNS verification a copy-paste job.
- Go to Sitemaps and submit
https://example.com/sitemap.xml. - Use URL Inspection on your homepage and click Request indexing.
For Bing, add the site in Bing Webmaster Tools and submit the same sitemap. You can also import from Google Search Console.
Do not expect instant results. Notion's own documentation says Notion Sites can take up to four weeks to be indexed. A custom domain with a submitted sitemap can be faster, but no one can promise a timeline.
5. Add analytics only if you will use it
Nooxy supports a GA4 measurement ID:
googleTagID: 'G-XXXXXXXXXX',
That is optional. If you install analytics, use it to learn which pages people actually read. Do not install three tools and then ignore all of them.
Updating your site later
There are two loops, and mixing them up causes most of the confusion.
| What you changed | What to run |
|---|---|
| Content in Notion | Nothing. Publish the page and the site updates, within the cache TTL if you turned on Workers Cache |
nooxy/config.js, head.css, head.js, body.js, or header.html
|
npx nooxy generate then npm run deploy
|
| Domain or DNS settings | Update them in Cloudflare and the domain field, then redeploy |
| A new page | Add it to slugToPage, then npx nooxy generate and npm run deploy
|
Content changes are live because Nooxy fetches the published Notion page on each request. Config and asset changes need a redeploy because they are compiled into the Worker.
With Workers Cache on, the edge can serve a copy for up to the TTL you set. Redeploying starts a fresh cache, because the Worker version is part of the cache key.
That is the maintenance model: almost none for content, a package update every few months, and a redeploy when you change the design or a route.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| 404 or "Page not found" | Page is not published, the ID is wrong, or notionDomain is wrong |
Publish the page, copy the 32-character ID again, and use the .notion.site hostname |
| Blank page | Usually a wrong page ID | Re-copy the ID from the published URL |
| CSS is not applied | You forgot to regenerate or redeploy | Run npx nooxy generate and npm run deploy
|
| SEO tags are missing |
seo.indexing is false, you are looking at the rendered DOM, or the Worker was not redeployed |
Set indexing: true, view the page source, and redeploy |
| Edited a Notion page but the site shows the old copy | Workers Cache is serving the cached HTML | Wait for the TTL, or redeploy to start a fresh cache. A zone "Purge Everything" does not clear the Worker cache |
Cannot find module 'nooxy' |
Nooxy is not installed in the project root | Run npm install nooxy inside my-nooxy-site
|
| Custom domain does not work | DNS is not active, an old record conflicts, or the domain is not attached to the Worker | Wait for Active, remove the conflicting record, and add the Custom Domain |
www does not work |
www is not attached, or there is no redirect |
Add www as a Custom Domain and configure subDomains
|
| Local server will not start | Wrong folder or port conflict |
cd into the project and stop the other process |
The pinned troubleshooting section and the Cloudflare guide's troubleshooting section cover more cases.
The main trade-offs
Nooxy is built for public, content-first sites. It does a few things well instead of trying to be every CMS. Here is the short list.
- The setup is terminal-first, but it is short. You paste the commands in this guide and edit one config file. After that, everyday updates happen in Notion. The payoff is full control over CSS, JavaScript, and SEO instead of a locked builder.
- Content is served live from Notion. That makes edits appear immediately and keeps database views interactive. Without caching, the site follows Notion's uptime: if Notion is unreachable, Nooxy returns a 503, not a stale copy. Workers Cache changes that trade: a cached page can survive a short outage, but edits can lag by the TTL. If you need a site that always stays up independently, a static export is still the better fit.
- It publishes public pages, not accounts. Nooxy does not bundle login, memberships, payments, or private content. Comments can be added with a third-party script. If your site needs accounts or a full application, pair Nooxy with another service or choose an app stack.
-
The domain is the only likely bill. Hosting, TLS, and the Workers free tier are $0. A custom domain is roughly $10 to $15 per year, and a
*.workers.devURL is free.
Nooxy is open source, MIT licensed, and supported through GitHub issues, with tests and CI in the repo. If those points fit your site, the rest of this guide is the fast path.
Nooxy vs Notion Sites, Super.so, and Fruition
| Option | What it costs | Best for | What to know |
|---|---|---|---|
| Nooxy + Cloudflare Workers | $0 hosting. Domain roughly $10 to $15 per year, or $0 on *.workers.dev
|
A free, live Notion site with full CSS, JS, and SEO control | Short terminal setup. Optional edge cache |
| Notion Sites | Free basic publishing. A Plus plan with the custom domain add-on is roughly $18 to $22 per month | The fastest official, click-based option | Custom domain and advanced SEO are paid |
| Super.so | Free on super.site. Custom domain plans start at $16 per month |
A polished static Notion site | Paid for a custom domain. Static export loses live database filtering |
| Fruition | Free | Existing legacy setups | Maintenance has slowed; check recent activity before starting |
| Custom static site | Hosting can be $0. Your time is the cost | Full control and a custom build | You own the build and maintenance |
A fair summary:
- Nooxy is a free, open-source option that keeps Notion live and gives you full design and SEO control. The trade-off is a short terminal setup. Content follows Notion uptime unless you turn on the optional edge cache.
- Notion Sites is the easiest official option. It is click-based and quick to launch; custom domains and advanced SEO are paid.
- Super.so is a polished static builder with a paid custom-domain plan. It is a good choice if you want a static export and do not need live database filtering.
- Fruition helped popularize this approach, but its maintenance has slowed. Check the recent activity before choosing it for a new site.
- A custom static site gives you maximum control if you enjoy owning the build pipeline.
Nooxy's sweet spot is free hosting, live Notion content, and full control, with a short setup.
Who Nooxy is for
Nooxy is a strong fit if:
- You already write in Notion and want one source of truth.
- You want a custom domain without a monthly hosting bill.
- You want full control over your CSS, JavaScript, and SEO tags.
- You want live Notion databases and filtering on your site.
- You are a PhD student, researcher, writer, consultant, or developer who needs a clean Notion portfolio website, lab site, or blog.
- You are comfortable copying a few terminal commands once.
Choose another tool if:
- You need a completely click-based setup and never want to open a terminal. Notion Sites is the closest fit.
- You need accounts, payments, private content, or a full web application. Nooxy is a publishing layer, not an app framework.
- You need a fully static site that stays online independently of Notion. A static export is the better fit.
If the second list sounds like your project, Notion Sites or Super.so are good choices for that job. Nooxy is the better fit when you want live Notion content, full control, and no monthly hosting bill.
FAQ
Is Nooxy really free?
Yes for hosting. Nooxy is MIT licensed, Cloudflare Workers has a free tier with 100,000 requests per day, and Notion's free plan is enough for a solo personal site. A custom domain is the only likely cost, usually $10 to $15 per year. The *.workers.dev URL costs nothing.
Do I need a Notion custom domain?
No. You can launch on the free *.workers.dev URL and add a domain later. A Notion custom domain looks more professional and helps with branding, but it is not required to get the site working.
Do I need to know how to code?
No for the setup. The guide gives you every command and one config file to edit. Customizing CSS or JavaScript is optional, and that is where code helps. The launch itself does not require writing code.
Can I use Notion databases on my website?
Yes. Nooxy serves the published Notion page, so database views and filtering that work on the public Notion page should work on your domain. It does not use the Notion API, and it does not convert the page to static HTML.
Do content changes appear automatically?
Yes. Edit and publish the page in Notion, and the site reflects the change on the next request. If you turned on Workers Cache, the change appears within the cache TTL. Changes to files in nooxy/ are different: run npx nooxy generate and npm run deploy.
Should I turn on Cloudflare caching?
Yes if you want faster loads and can wait a few minutes for an edit to appear. No if you serve several domains from one Worker with configKey, because the cache key does not include the hostname. The Optional extras section has a five-minute setup that leaves browser caching alone.
Is a Nooxy site good for SEO?
A Nooxy site serves your published Notion website on your own domain and gives you the technical pieces: no noindex, canonical URLs, clean paths, per-page titles and descriptions, Open Graph and Twitter tags, a sitemap, robots.txt, and a WebPage JSON-LD block with a nested WebSite. Rankings still depend on your content, your competition, and your links. No tool can guarantee a position.
How is this different from Notion Sites and Super.so?
Notion Sites is the official, click-based option. Custom domains and advanced SEO are paid. Super.so is a polished static site builder for Notion, also paid for a custom domain. Nooxy is free and open source, keeps live Notion content, and gives you full CSS, JS, and SEO control. The launch is terminal-based, and the guide walks through every command.
What happens if Notion goes down?
Without caching, Nooxy fetches the published page on each request, so your site follows Notion's uptime. That is the trade-off for live edits and interactive databases. If Notion is unreachable, Nooxy returns a 503 rather than a stale copy. With Workers Cache on, a recently cached page can survive a short outage, but edits can take up to the TTL to appear. If you need a fully static fallback, a static export is the better fit.
Can I use Cloudflare Pages instead of Workers?
Not with this guide. Cloudflare Pages is built for static assets. Nooxy needs a Worker because it proxies requests and rewrites the HTML response. The guide uses Workers for that reason.
How do I move away later?
Your content stays in Notion, and your domain stays yours. Export your Notion pages, point the domain at the new host, and update the domain field if you keep Nooxy around. There is no proprietary database to migrate.
The 10-minute checklist
- [ ] Node.js 22 or newer installed
- [ ] Cloudflare account created
- [ ] Notion page published, with subpage permissions checked
- [ ]
npm create cloudflare@latest my-nooxy-site - [ ]
npm install nooxyandnpx nooxy init - [ ] Page IDs and
notionDomaincopied - [ ]
nooxy/config.jsedited - [ ]
npx nooxy generaterun - [ ]
src/index.tsreplaced - [ ]
npm run devtested atlocalhost:8787 - [ ]
npx wrangler loginandnpm run deploycompleted - [ ] Custom domain and
wwwredirect configured - [ ]
pageMetadataadded - [ ] Sitemap submitted to Google Search Console
Final thoughts
Most people do not need a custom CMS. They need a place to write and a boring pipe to the web.
If you already write in Notion, Nooxy is that pipe. It is not the right tool for every site, but for a public, content-first personal site it is a strong fit. If you want a site you can set up in about 10 minutes, host for $0, and update from Notion without touching a build pipeline, this is the fast path.
If you try it:
- Star the repo if it saves you money or time.
- Open an issue if something breaks. Include the error and the page ID format, never private content.
- Share your site in Discussions. I genuinely like seeing what people build.
- Read the pinned Cloudflare guide if you want the longer version of this tutorial.
What are you putting online: a portfolio, a lab notebook, or a blog? Tell me in the comments. I read them.
About the author: David Raphi is a full-stack engineer and the maintainer of Nooxy. He has shipped systems for US government agencies and leads election-critical platforms. He writes about Notion, Cloudflare, and zero-dependency tooling.
GitHub · X · LinkedIn · contact@draphy.org
Top comments (0)