Nooxy is a modern, open-source, zero dependency Notion reverse proxy that allows you to host your Notion pages on your own custom domain with complete control over customization. Built with TypeScript and designed for Cloudflare Workers and modern Node.js runtimes, Nooxy provides a powerful alternative to Notion's expensive custom domain feature.
Notion's custom domain feature is expensive and offers limited customization control. Nooxy solves this by providing:
- π° Completely Free - No monthly fees or usage limits
- π¦ Zero Dependencies - No external dependencies, lightweight and fast
- π¨ Full Customization - Inject custom CSS, JavaScript, and HTML with metadata customization
- π§ Local Development - Test your site locally before deployment
- β‘ High Performance - Specially Built for Cloudflare Workers edge computing and modern Node.js runtimes
- π οΈ Developer Friendly - CLI tools, TypeScript support, and extensive configuration options
- π Seamless Navigation - Proper URL rewriting and internal link handling
- π SEO & Metadata - Advanced metadata customization for better search engine optimization
Nooxy supports local development with automatic localhost detection:
// Automatically detects localhost and adjusts domain
const proxy = initializeNooxy({
domain: 'your-domain.com', // Will be overridden to localhost:port in dev
// ... other config
});
- Test your Notion site locally before deployment
- Hot reloading and instant feedback
- Debug and iterate quickly
Supported via initializeNooxy({ configKey, config })
and a cached ConfigManager
keyed by configKey
. This lets you run multiple isolated proxies (e.g., different domains) in the same runtime with independent caches.
// Production config
const prodProxy = initializeNooxy({
configKey: 'production',
config: productionConfig,
});
// Development config
const devProxy = initializeNooxy({
configKey: 'development',
config: developmentConfig,
});
// Usage example in a multi-tenant Worker
export default {
async fetch(request) {
const host = new URL(request.url).hostname;
const proxy = host.endsWith('example.com') ? prodProxy : devProxy;
return proxy(request);
},
};
Why it's useful:
- Run multiple domains/sites with a single deployment
- Separate caches for each site via `ConfigManager.getInstance(config, configKey)`
- Easier staging/production side-by-side in one process
Nooxy provides sophisticated URL rewriting that handles:
- Internal Links: All Notion internal links are rewritten to your domain
- Navigation: Seamless internal navigation within your custom domain and Browser back/forward buttons work correctly
- Deep Linking: Direct links to specific pages work seamlessly
- Slug Mapping: Proper slug-to-page mapping with 301 redirects i.e Clean URLs like
/about
map to Notion page IDs
npx nooxy init
- Initialize configuration files in anooxy/
folder at project rootnpx nooxy generate [--path=/absolute/or/relative/path] [--no-minify]
- Generate minified string files from a custom path (defaults to current working directory). The configuration folder name must benooxy
.
- Inject custom HTML, CSS, and JavaScript into the header
- Complete control over the top navigation bar
- Responsive design with mobile optimization
- Smart configuration caching for better performance
- Multiple instance support for different environments
- Efficient memory usage
- Zero dependencies for minimal bundle size and fast loading
- Automatic minification of custom CSS, JavaScript, and HTML files
- Advanced minification that preserves functionality while reducing file sizes
- Required custom files: CSS, JavaScript, and HTML injection for complete control
- Custom CSS injection for styling
- JavaScript injection for functionality (both head and body)
- HTML header customization
- Metadata customization: Page-specific SEO metadata, Open Graph tags, Twitter cards, and JSON-LD structured data
- Optional Google Fonts integration
- Optional Google Analytics support
- Proper XMLHttpRequest handling
- Blocked problematic Notion requests
- Content Security Policy management
- Page-specific SEO: Customize title, description, and Open Graph tags for each page
- Social Media Optimization: Twitter cards and Facebook Open Graph metadata
- JSON-LD Structured Data: Automatic generation of structured data for better search visibility
- SEO Optimization: Enhanced search engine optimization with customizable meta tags
- No External Dependencies: Zero runtime dependencies for maximum compatibility
- Minimal Bundle Size: Ultra-lightweight package for fast loading
- Security: No third-party dependencies means fewer security vulnerabilities
- Reliability: No dependency conflicts or version mismatches
- Performance: Faster cold starts and reduced memory usage
- Deployment: Easy deployment to any environment without dependency management
- Node.js
- A Cloudflare account (for deployment)
- A custom domain (optional, for production)
npm install nooxy
# or
pnpm add nooxy
# or
yarn add nooxy
Zero Dependencies: Nooxy has no runtime dependencies, making it lightweight and fast to install.
npx nooxy init
This creates a nooxy
directory with all necessary configuration files:
nooxy/
βββ config.js # Main configuration file
βββ head.js # Custom JavaScript for <head>
βββ body.js # Custom JavaScript for <body>
βββ head.css # Custom CSS styles
βββ header.html # Custom HTML header
βββ generated/ # Auto-generated files (created after running generate)
βββ _head-js-string.js
βββ _body-js-string.js
βββ _head-css-string.js
βββ _header-html-string.js
Edit nooxy/config.js
:
import { HEAD_JS_STRING } from './generated/_head-js-string.js';
import { BODY_JS_STRING } from './generated/_body-js-string.js';
import { HEAD_CSS_STRING } from './generated/_head-css-string.js';
import { HEADER_HTML_STRING } from './generated/_header-html-string.js';
/** @type {import('nooxy').NooxySiteConfig} */
export const SITE_CONFIG = {
// Site domain, example.com
domain: 'your-domain.com',
// Map slugs (short page names) to Notion page IDs
// '/' slug is your root page
slugToPage: {
'/': 'NOTION_HOME_PAGE_ID',
// '/contact': 'NOTION_PAGE_ID',
// '/about': 'NOTION_PAGE_ID',
// Hint: you can use '/' in slug name to create subpages
// '/about/people': 'NOTION_PAGE_ID',
},
// SEO metadata
siteName: 'Your Site Name',
// Additional safety: avoid serving extraneous Notion content from your website
// Use the value from your Notion like example.notion.site
notionDomain: 'example.notion.site',
// Optional: Page-specific metadata customization for SEO
// pageMetadata: {
// 'NOTION_PAGE_ID': {
// title: 'My Custom Page Title',
// description: 'My custom page description',
// image: 'https://imagehosting.com/images/page_preview.jpg',
// author: 'My Name',
// },
// },
// Optional: 404 page configuration
// fof: {
// page: "NOTION_PAGE_ID",
// slug: "404", // default
// },
// Optional: Subdomain redirects
// subDomains: {
// www: {
// redirect: 'https://your-domain.com',
// },
// },
// Optional: Google Font and Analytics
// googleFont: 'Roboto',
// googleTagID: 'GOOGLE_TAG_ID',
// Required: Custom JS, CSS, HTML for head and body of a Notion page
customHeadCSS: HEAD_CSS_STRING,
customHeadJS: HEAD_JS_STRING,
customBodyJS: BODY_JS_STRING,
customHeader: HEADER_HTML_STRING,
};
# Run from project root (where the `nooxy/` folder exists)
npx nooxy generate
# Or specify a custom path that contains the `nooxy/` folder
npx nooxy generate --path=./examples/cloudflare
# Disable minification (optional)
npx nooxy generate --no-minify
This reads files from <path-or-cwd>/nooxy/{head.js,body.js,head.css,header.html}
and converts them into minified string constants under <path-or-cwd>/nooxy/generated/
. The generated files are automatically minified for optimal performance.
Create a Cloudflare Worker and use the following code (Edge runtime):
import { initializeNooxy } from 'nooxy';
import { SITE_CONFIG } from './nooxy/config';
const proxy = initializeNooxy(SITE_CONFIG);
export default {
async fetch(request: Request): Promise<Response> {
return await proxy(request);
},
} satisfies ExportedHandler<Env>;
Or use in a modern Node.js runtime (e.g., express-like frameworks that support Request
/Response
or via polyfills):
import { initializeNooxy } from 'nooxy';
import { SITE_CONFIG } from './nooxy/config';
import http from 'node:http';
const proxy = initializeNooxy(SITE_CONFIG);
const server = http.createServer(async (req, res) => {
const url = `http://${req.headers.host}${req.url}`;
const request = new Request(url, { method: req.method });
const response = await proxy(request);
res.statusCode = response.status;
response.headers.forEach((v, k) => res.setHeader(k, v));
const body = await response.arrayBuffer();
res.end(Buffer.from(body));
});
server.listen(8787, () =>
console.log('Nooxy Node server on http://localhost:8787')
);
Field | Type | Required | Description |
---|---|---|---|
domain |
string |
β | Your custom domain (e.g., example.com ) |
siteName |
string |
β | Site name for SEO and social sharing |
slugToPage |
Record<string, string> |
β | Mapping of URL slugs to Notion page IDs |
notionDomain |
string |
β | Your Notion workspace domain |
Field | Type | Required | Description |
---|---|---|---|
customHeadCSS |
string |
β | Custom CSS for <head> |
customHeadJS |
string |
β | Custom JavaScript for <head> |
customBodyJS |
string |
β | Custom JavaScript for <body> |
customHeader |
string |
β | Custom HTML header content |
Field | Type | Required | Description |
---|---|---|---|
pageMetadata |
Record<string, PageMetadata> |
β | Page-specific metadata customization for SEO |
siteIcon |
string |
β | Custom favicon URL |
twitterHandle |
string |
β | X (formerly Twitter) handle for social sharing |
subDomains |
Record<string, SubDomainRedirect> |
β | Subdomain redirect configuration |
fof |
FofConfig |
β | 404 page configuration |
googleFont |
string |
β | Google Font family name |
googleTagID |
string |
β | Google Analytics measurement ID |
interface PageMetadata {
title?: string; // Page title customization for SEO
description?: string; // Page description customization for SEO
image?: string; // Page-specific Open Graph image customization
author?: string; // Page author metadata customization
}
npx nooxy init
Creates the initial configuration files in a nooxy
directory at the project root. The folder name is fixed to nooxy
.
npx nooxy generate [--path=/custom/path] [--no-minify]
Converts your custom files (head.js
, body.js
, head.css
, header.html
) into minified importable string constants under <path-or-cwd>/nooxy/generated/
.
Options:
--path
: Specify a custom directory path that contains anooxy/
folder--no-minify
: Disable automatic minification of generated files
Edit nooxy/head.css
to add custom styles:
/* Hide Notion's default top bar */
.notion-topbar {
display: none !important;
}
/* Custom header styling */
.nooxyBadge_4f7c2b1a-demo-topbar {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px;
color: white;
}
/* Custom page styling */
.notion-page-content {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
Edit nooxy/body.js
for page functionality:
// Custom page interactions
document.addEventListener('DOMContentLoaded', function () {
// Add custom functionality here
console.log('Nooxy page loaded!');
// Example: Add smooth scrolling
document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({ behavior: 'smooth' });
}
});
});
});
Use nooxy/head.js
to inject scripts that must load early in the <head>
(e.g., analytics, tag managers, A/B testing beacons). These run before body scripts:
// Example: preload analytics or feature flags
// Runs in <head>
(function () {
console.log('Head script loaded');
// e.g., initialize a feature flag SDK
})();
Edit nooxy/header.html
for custom header content:
<!-- Custom navigation -->
<nav class="custom-nav">
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
<!-- Custom branding -->
<div class="custom-branding">
<h1>Your Brand</h1>
</div>
Nooxy automatically minifies your custom files during generation for optimal performance:
- JavaScript minification: Removes comments, unnecessary whitespace, and optimizes code
- CSS minification: Removes comments, optimizes selectors, and compresses styles
- HTML minification: Removes unnecessary whitespace while preserving functionality
- Smart preservation: Protects important content like URLs, strings, and script tags
- Size reduction: Typically achieves 20-40% file size reduction
To disable minification, use the --no-minify
flag:
npx nooxy generate --no-minify
Nooxy provides advanced metadata customization for better SEO and social media sharing:
Configure custom metadata for individual pages using the pageMetadata
option:
// In your config.js
export const SITE_CONFIG = {
// ... other config
pageMetadata: {
NOTION_PAGE_ID: {
title: 'Custom Page Title - Your Site',
description: 'Custom page description for SEO',
image: 'https://your-domain.com/custom-og-image.jpg',
author: 'Your Name',
},
},
};
- Search Engine Optimization: Custom titles and descriptions for better search rankings
- Social Media Sharing: Custom Open Graph images and descriptions for Twitter, Facebook, LinkedIn
- Structured Data: Automatic JSON-LD generation for rich snippets
- Dynamic Content: Real-time metadata rewriting based on page content
- Title Tags: Custom page titles for search engines
- Meta Descriptions: Custom descriptions for search results
- Open Graph: Facebook, LinkedIn sharing optimization
- Twitter Cards: Twitter sharing optimization
- Author Information: Page author metadata
- Custom Images: Page-specific social media images
src/
βββ index.ts # Main export file
βββ proxy.ts # Core reverse proxy logic
βββ types.ts # TypeScript type definitions
βββ helpers/ # Utility functions
β βββ config-loader.ts # Configuration management with caching
β βββ index.ts # URL handling, localhost detection, and helper utilities
βββ handlers/ # Request handlers
β βββ handle-favicon.ts # Favicon handling
β βββ handle-options.ts # CORS preflight handling
β βββ handle-sitemap.ts # Sitemap generation
β βββ index.ts # Handler exports
βββ rewriters/ # HTML content rewriting
β βββ data-rewriter.ts # Response data rewriting and script injection
β βββ header-rewriter.ts# Request/response header modification
β βββ meta-rewriter.ts # Meta tag rewriting and SEO metadata customization
β βββ index.ts # Rewriter exports
β βββ custom/ # Custom styling and scripts
β βββ generated/ # Auto-generated minified strings (created by CLI)
β β βββ _head-css-string.ts # Minified CSS string
β β βββ _head-js-string.ts # Minified JS string
β βββ head.css # Default custom CSS styles
β βββ head.js # Default custom JavaScript
βββ lib/ # Core libraries
β βββ minify.js # JavaScript minification utilities
β βββ minify.d.ts # TypeScript definitions for minify
βββ cli/ # Command-line interface
βββ index.ts # CLI entry point
βββ init.ts # Initialize command
βββ generate.ts # Generate command with minification
βββ templates/ # Configuration templates
βββ config.js # Main config template
βββ head.js # Head JS template
βββ body.js # Body JS template
βββ head.css # CSS template
βββ header.html # Header HTML template
- Request Interception: Nooxy intercepts requests to your custom domain
- URL Mapping: Maps clean URLs to Notion page IDs using your configuration
- Content Fetching: Fetches content from Notion's servers
- HTML Rewriting: Uses string replacement and regex to modify content in real-time
- Metadata Customization: Rewrites meta tags, Open Graph, Twitter cards, and JSON-LD structured data
- Header Modification: Modifies request/response headers for proper domain handling
- Script Injection: Injects custom CSS, JavaScript, and header content
- Response Delivery: Serves the modified content to your visitors
- Multi-instance Support:
ConfigManager
class with instance caching keyed byconfigKey
- Smart Caching: Processed configurations are cached to improve performance
- Dynamic Processing: Automatically builds helper indexes (slugs, pageToSlug mapping)
- Data Rewriter: Handles response data modification, script injection, and URL rewriting
- Header Rewriter: Manages request/response header modifications for domain handling
- Meta Rewriter: Specialized SEO metadata customization and JSON-LD structured data
- Template Generation: Creates initial configuration files from templates
- File Processing: Converts custom files to minified TypeScript string constants
- Minification: Advanced minification for JavaScript, CSS, and HTML files
- Favicon Handler: Proxies custom favicon requests
- Sitemap Handler: Automatically generates XML sitemaps
- Options Handler: Manages CORS preflight requests
Nooxy is inspired by Fruition and NoteHost but adds:
- A maintained TypeScript package API for embedding into different runtimes
- Zero dependencies for minimal bundle size and fast deployment
- CLI with
init
(scaffold config) andgenerate
(convert custom files to strings), with--path
support on generate - Multi-instance configuration with caching keyed by
configKey
for multi-tenant or multi-env setups - Robust content rewriting system with specialized handlers for different content types
- Advanced metadata customization with page-specific SEO, Open Graph, Twitter cards, and JSON-LD structured data
- Client-side navigation and URL rewriting that keeps users on your domain and preserves slugs
- Automatic minification system for optimal performance and reduced bundle sizes
- Local development detection with domain normalization for smooth localhost experience
Please see CONTRIBUTING.md for full guidelines, development setup, commit conventions, and PR requirements.
Problem: Your Notion pages return 404 errors.
Solution:
- Verify your Notion page IDs are correct
- Ensure your Notion pages are published publicly
- Check that your
notionDomain
is set correctly
Problem: Your custom CSS isn't being applied.
Solution:
- Run
npx nooxy generate
after making CSS changes - Check that your CSS selectors are specific enough
- Use
!important
for overriding Notion's styles - Ensure
customHeadCSS
is properly set in your config
Problem: Custom JavaScript isn't executing.
Solution:
- Ensure your JavaScript is in the correct file (
head.js
orbody.js
) - Run
npx nooxy generate
after making changes - Check browser console for errors
- Ensure
customHeadJS
andcustomBodyJS
are properly set in your config
Problem: Getting errors about missing required fields.
Solution:
- Ensure all required fields are set:
domain
,siteName
,slugToPage
,notionDomain
,customHeadCSS
,customHeadJS
,customBodyJS
,customHeader
- Run
npx nooxy generate
to create the required string files - Check that your
nooxy/
folder contains all template files
Problem: Local development server not working.
Solution:
- Ensure you're using Node.js 22.0.0 or higher
- Check that your
wrangler.toml
is configured correctly - Verify your Notion pages are accessible
- GitHub Issues: Create an issue
- Discussions: GitHub Discussions
- Email: [email protected]
This project is licensed under the MIT License - see the LICENSE file for details.
- Fruition: Inspired by the original Fruition project by @stephenou
- NoteHost: Built upon concepts from @velsa's NoteHost
- Community: Thanks to all contributors and users who help improve Nooxy
- os.draphy.org
- Add your site here (Submit a PR to add your site!)
- Portfolio Sites: Perfect for developer portfolios and personal websites with metadata customization
- Documentation: Great for technical documentation and wikis with SEO optimization
- Blogs: Excellent for personal and professional blogs with custom metadata
- Landing Pages: Ideal for product landing pages and marketing sites with advanced SEO
Nooxy is built for performance:
- Edge Computing: Runs on Cloudflare's global edge network
- Zero Dependencies: No external dependencies for minimal bundle size
- Optimized Caching: Smart configuration caching reduces overhead
- Minimal Bundle Size: Lightweight and fast
- TypeScript: Type safety and better performance
- Metadata Optimization: Advanced SEO metadata customization for better search rankings
This repository includes runnable examples:
examples/cloudflare
- A Cloudflare Workers example integrating Nooxy withinitializeNooxy
andwrangler
configuration. More runtime examples (e.g., Bun, Node HTTP, Express) will be added soon.