refactor: standardize Cloudflare proxy implementation, clean up configuration, and update documentation
Browse files- .env.example +1 -1
- CHANGELOG.md +21 -17
- CLOUDFLARE_PROXY_GUIDE.md +0 -44
- CODE_OF_CONDUCT.md +2 -0
- README.md +11 -8
- cloudflare-proxy.js +48 -26
- cloudflare-worker.js +14 -8
- dns-fix.js +6 -2
.env.example
CHANGED
|
@@ -42,7 +42,7 @@ N8N_LOG_LEVEL=error
|
|
| 42 |
# Your Cloudflare Worker URL (e.g. h8n-proxy.somrat.workers.dev)
|
| 43 |
CLOUDFLARE_PROXY_URL=
|
| 44 |
# Comma-separated list of domains to proxy. Use "*" to proxy everything.
|
| 45 |
-
|
| 46 |
# BUILD-TIME VARIABLE (HF Spaces: add as Variable, not Secret)
|
| 47 |
# -----------------------------------------------------------------------------
|
| 48 |
|
|
|
|
| 42 |
# Your Cloudflare Worker URL (e.g. h8n-proxy.somrat.workers.dev)
|
| 43 |
CLOUDFLARE_PROXY_URL=
|
| 44 |
# Comma-separated list of domains to proxy. Use "*" to proxy everything.
|
| 45 |
+
CLOUDFLARE_PROXY_DOMAINS=api.telegram.org,discord.com,discordapp.com
|
| 46 |
# BUILD-TIME VARIABLE (HF Spaces: add as Variable, not Secret)
|
| 47 |
# -----------------------------------------------------------------------------
|
| 48 |
|
CHANGELOG.md
CHANGED
|
@@ -2,28 +2,32 @@
|
|
| 2 |
|
| 3 |
All notable changes to this project will be documented in this file.
|
| 4 |
|
| 5 |
-
## [1.0.0] - 2026-04-
|
| 6 |
|
| 7 |
### π Initial Release
|
| 8 |
|
| 9 |
#### Features
|
| 10 |
|
| 11 |
-
- **Self-hosted n8n** β
|
| 12 |
-
- **Persistent
|
| 13 |
-
- **
|
| 14 |
-
- **
|
| 15 |
-
- **
|
| 16 |
-
- **
|
| 17 |
-
- **
|
| 18 |
-
- **
|
| 19 |
-
- **
|
| 20 |
-
- **
|
| 21 |
-
- **Optional n8n version pinning** β pass `N8N_VERSION` as a HF Space Variable to pin a specific n8n release
|
| 22 |
|
| 23 |
#### Architecture
|
| 24 |
|
| 25 |
-
- `Dockerfile` β
|
| 26 |
-
- `start.sh` β
|
| 27 |
-
- `
|
| 28 |
-
- `
|
| 29 |
-
- `
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
All notable changes to this project will be documented in this file.
|
| 4 |
|
| 5 |
+
## [1.0.0] - 2026-04-24
|
| 6 |
|
| 7 |
### π Initial Release
|
| 8 |
|
| 9 |
#### Features
|
| 10 |
|
| 11 |
+
- **Self-hosted n8n** β Runs the latest n8n on HuggingFace Spaces Docker using SQLite (no external DB required).
|
| 12 |
+
- **Persistent Backup** β Automatically syncs the entire n8n workspace (workflows, credentials, database) to a private HF Dataset.
|
| 13 |
+
- **Cloudflare Transparent Proxy** β Built-in fix to bypass platform network blocks for services like Telegram and Discord.
|
| 14 |
+
- **DNS-over-HTTPS (DoH)** β Automatic fallback resolution for domains blocked at the DNS level (e.g., WhatsApp, Telegram).
|
| 15 |
+
- **Premium Dashboard** β Beautiful web interface at `/` for real-time uptime monitoring and sync health tracking.
|
| 16 |
+
- **Built-in Keep-Alive** β Integrated UptimeRobot setup tool directly from the dashboard to prevent free HF Spaces from sleeping.
|
| 17 |
+
- **Native Security** β Optimized for n8n v2 native user management with hardened file permissions (`umask 0077`).
|
| 18 |
+
- **Safe Persistence** β Uses atomic SQLite backups to ensure data integrity during periodic syncs.
|
| 19 |
+
- **Auto-Restore** β Seamlessly pulls the latest state from your HF Dataset on every startup.
|
| 20 |
+
- **Graceful Shutdown** β Ensures a final backup pass is completed on `SIGTERM` / `SIGINT` before the container exits.
|
|
|
|
| 21 |
|
| 22 |
#### Architecture
|
| 23 |
|
| 24 |
+
- `Dockerfile` β Optimized build on `node:22-slim` including all n8n and sync dependencies.
|
| 25 |
+
- `start.sh` β Orchestrates startup, validates environment, and manages service lifecycle.
|
| 26 |
+
- `health-server.js` β High-performance namespace proxy and dashboard server.
|
| 27 |
+
- `cloudflare-proxy.js` β Transparently intercepts and routes blocked traffic via Cloudflare Workers.
|
| 28 |
+
- `dns-fix.js` β Monkey-patches Node.js DNS for reliable DoH fallback.
|
| 29 |
+
- `n8n-sync.py` β Robust background sync engine using the `huggingface_hub` API.
|
| 30 |
+
- `start.sh` β Configures environment, restores backup, and launches background sync loop.
|
| 31 |
+
|
| 32 |
+
---
|
| 33 |
+
*Made with β€οΈ by [@somratpro](https://github.com/somratpro)*
|
CLOUDFLARE_PROXY_GUIDE.md
DELETED
|
@@ -1,44 +0,0 @@
|
|
| 1 |
-
# π Cloudflare Proxy Guide
|
| 2 |
-
|
| 3 |
-
Hugging Face Spaces officially blocks outgoing connections to specific services like **Telegram**, **WhatsApp**, and **Discord** on the free tier.
|
| 4 |
-
|
| 5 |
-
Hugging8n includes a built-in **Transparent Cloudflare Proxy** that allows you to bypass these restrictions using a single Cloudflare Worker.
|
| 6 |
-
|
| 7 |
-
---
|
| 8 |
-
|
| 9 |
-
## π Setup in 2 Minutes
|
| 10 |
-
|
| 11 |
-
### Step 1: Deploy your Cloudflare Worker
|
| 12 |
-
|
| 13 |
-
1. Log in to [dash.cloudflare.com](https://dash.cloudflare.com/).
|
| 14 |
-
2. Go to **Workers & Pages** -> **Create Worker**.
|
| 15 |
-
3. Name it (e.g., `h8n-proxy`).
|
| 16 |
-
4. Paste the code from [cloudflare-worker.js](./cloudflare-worker.js) into the editor.
|
| 17 |
-
5. Click **Deploy**.
|
| 18 |
-
6. Copy your Worker URL (e.g., `h8n-proxy.yourname.workers.dev`).
|
| 19 |
-
|
| 20 |
-
### Step 2: Configure Hugging8n
|
| 21 |
-
|
| 22 |
-
Go to your Space **Settings** -> **Variables** (or Secrets) and add:
|
| 23 |
-
|
| 24 |
-
1. **`CLOUDFLARE_PROXY_URL`** (Required):
|
| 25 |
-
- Value: `h8n-proxy.yourname.workers.dev` (You can omit the `https://`).
|
| 26 |
-
|
| 27 |
-
2. **`CLOUDFLARE_PROXY_DOMAINS`** (Optional):
|
| 28 |
-
- **Default**: Proxies Telegram and Discord only.
|
| 29 |
-
- **Wildcard Mode**: Set this to `*` to proxy **every single request** n8n makes to the outside world.
|
| 30 |
-
|
| 31 |
-
### Step 3: Restart Space
|
| 32 |
-
|
| 33 |
-
Hugging8n will now automatically intercept all requests to the blocked domains and route them through your worker. **You do not need to change any URLs inside your n8n workflows.**
|
| 34 |
-
|
| 35 |
-
---
|
| 36 |
-
|
| 37 |
-
## π οΈ How it Works
|
| 38 |
-
|
| 39 |
-
1. **DNS Fix**: Hugging8n uses a built-in DNS-over-HTTPS (DoH) resolver to get the real IPs of blocked domains, bypassing HF's sinkholed DNS.
|
| 40 |
-
2. **SNI Bypass**: The transparent proxy changes the "label" (SNI) of your traffic to match your Cloudflare domain. Hugging Face sees you connecting to Cloudflare (allowed) instead of Telegram/Discord (blocked).
|
| 41 |
-
3. **Universal Routing**: Using the `x-target-host` header, one single worker can handle multiple different services dynamically.
|
| 42 |
-
|
| 43 |
-
---
|
| 44 |
-
*If you upgrade to a paid Space, these firewalls are removed and you no longer need this proxy.*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CODE_OF_CONDUCT.md
CHANGED
|
@@ -7,12 +7,14 @@ We are committed to making participation in this project a harassment-free exper
|
|
| 7 |
## Our Standards
|
| 8 |
|
| 9 |
**Positive behavior includes:**
|
|
|
|
| 10 |
- Using welcoming and inclusive language
|
| 11 |
- Being respectful of differing viewpoints
|
| 12 |
- Gracefully accepting constructive criticism
|
| 13 |
- Focusing on what is best for the community
|
| 14 |
|
| 15 |
**Unacceptable behavior includes:**
|
|
|
|
| 16 |
- Trolling, insulting, or derogatory comments
|
| 17 |
- Public or private harassment
|
| 18 |
- Publishing others' private information without permission
|
|
|
|
| 7 |
## Our Standards
|
| 8 |
|
| 9 |
**Positive behavior includes:**
|
| 10 |
+
|
| 11 |
- Using welcoming and inclusive language
|
| 12 |
- Being respectful of differing viewpoints
|
| 13 |
- Gracefully accepting constructive criticism
|
| 14 |
- Focusing on what is best for the community
|
| 15 |
|
| 16 |
**Unacceptable behavior includes:**
|
| 17 |
+
|
| 18 |
- Trolling, insulting, or derogatory comments
|
| 19 |
- Public or private harassment
|
| 20 |
- Publishing others' private information without permission
|
README.md
CHANGED
|
@@ -58,11 +58,12 @@ secrets:
|
|
| 58 |
Navigate to your new Space's **Settings**, scroll down to **Variables and secrets**, and add:
|
| 59 |
|
| 60 |
- `HF_TOKEN` β Your HuggingFace token with **Write** access (to enable automatic backup).
|
| 61 |
-
- `CLOUDFLARE_PROXY_URL` β *(Optional but Recommended)* Your Cloudflare Worker URL to bypass platform blocks.
|
| 62 |
|
| 63 |
### Step 3: Deploy & Initialize
|
| 64 |
|
| 65 |
The Space will build and start automatically. Once ready:
|
|
|
|
| 66 |
1. Visit the Space URL.
|
| 67 |
2. Click **Open n8n Editor**.
|
| 68 |
3. Create your **Owner account** (this is your primary login).
|
|
@@ -70,6 +71,7 @@ The Space will build and start automatically. Once ready:
|
|
| 70 |
### Step 4: Monitor & Manage
|
| 71 |
|
| 72 |
Use the built-in dashboard at the root URL (`/`) to track:
|
|
|
|
| 73 |
- **Uptime:** Real-time uptime monitoring.
|
| 74 |
- **Sync Status:** Visual indicators for your workflow backups.
|
| 75 |
- **Keep-Alive:** Setup tool for external monitors.
|
|
@@ -79,16 +81,16 @@ Use the built-in dashboard at the root URL (`/`) to track:
|
|
| 79 |
Hugging Face Free Tier blocks outgoing connections to some services (Telegram, Discord, etc.). Hugging8n includes a transparent proxy system to bypass this.
|
| 80 |
|
| 81 |
1. Go to [Cloudflare Workers](https://dash.cloudflare.com/?to=/:account/workers-and-pages).
|
| 82 |
-
2. Create a new Worker
|
| 83 |
-
3.
|
| 84 |
-
4.
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
|
| 89 |
## πΎ Persistent Backup
|
| 90 |
|
| 91 |
-
Hugging8n automatically creates a private dataset named `hugging8n-backup` in your Hugging Face account.
|
| 92 |
|
| 93 |
- **Restore:** On startup, it pulls the latest state from your dataset.
|
| 94 |
- **Sync:** Periodically (every 3 minutes by default), it pushes updates to the dataset.
|
|
@@ -130,6 +132,7 @@ cp .env.example .env
|
|
| 130 |
```
|
| 131 |
|
| 132 |
**With Docker:**
|
|
|
|
| 133 |
```bash
|
| 134 |
docker build -t hugging8n .
|
| 135 |
docker run -p 7861:7861 --env-file .env hugging8n
|
|
|
|
| 58 |
Navigate to your new Space's **Settings**, scroll down to **Variables and secrets**, and add:
|
| 59 |
|
| 60 |
- `HF_TOKEN` β Your HuggingFace token with **Write** access (to enable automatic backup).
|
| 61 |
+
- `CLOUDFLARE_PROXY_URL` β *(Optional but Recommended)* Your Cloudflare Worker URL to bypass platform blocks. check [Setup Guide](#-cloudflare-proxy-setup).
|
| 62 |
|
| 63 |
### Step 3: Deploy & Initialize
|
| 64 |
|
| 65 |
The Space will build and start automatically. Once ready:
|
| 66 |
+
|
| 67 |
1. Visit the Space URL.
|
| 68 |
2. Click **Open n8n Editor**.
|
| 69 |
3. Create your **Owner account** (this is your primary login).
|
|
|
|
| 71 |
### Step 4: Monitor & Manage
|
| 72 |
|
| 73 |
Use the built-in dashboard at the root URL (`/`) to track:
|
| 74 |
+
|
| 75 |
- **Uptime:** Real-time uptime monitoring.
|
| 76 |
- **Sync Status:** Visual indicators for your workflow backups.
|
| 77 |
- **Keep-Alive:** Setup tool for external monitors.
|
|
|
|
| 81 |
Hugging Face Free Tier blocks outgoing connections to some services (Telegram, Discord, etc.). Hugging8n includes a transparent proxy system to bypass this.
|
| 82 |
|
| 83 |
1. Go to [Cloudflare Workers](https://dash.cloudflare.com/?to=/:account/workers-and-pages).
|
| 84 |
+
2. Create a new Worker using "Start with Hello World!" template
|
| 85 |
+
3. choose worker name (e.g. h8n-proxy) and deploy.
|
| 86 |
+
4. Click on "Edit Code" button, paste the code from [cloudflare-worker.js](./cloudflare-worker.js).
|
| 87 |
+
5. Click on "Deploy" button.
|
| 88 |
+
6. Copy the Worker URL (e.g., `https://h8n-proxy.yourname.workers.dev`).
|
| 89 |
+
7. Add this URL as the `CLOUDFLARE_PROXY_URL` secret in your Hugging8n Space settings.
|
| 90 |
|
| 91 |
## πΎ Persistent Backup
|
| 92 |
|
| 93 |
+
Hugging8n automatically creates a private dataset named `hugging8n-backup` in your Hugging Face account.
|
| 94 |
|
| 95 |
- **Restore:** On startup, it pulls the latest state from your dataset.
|
| 96 |
- **Sync:** Periodically (every 3 minutes by default), it pushes updates to the dataset.
|
|
|
|
| 132 |
```
|
| 133 |
|
| 134 |
**With Docker:**
|
| 135 |
+
|
| 136 |
```bash
|
| 137 |
docker build -t hugging8n .
|
| 138 |
docker run -p 7861:7861 --env-file .env hugging8n
|
cloudflare-proxy.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
/**
|
| 2 |
* Cloudflare Proxy: Transparent Fix for Blocked Domains
|
| 3 |
-
*
|
| 4 |
* Patches https.request to redirect traffic for Telegram/Discord
|
| 5 |
* through a Cloudflare Worker proxy.
|
| 6 |
*/
|
|
@@ -9,16 +9,22 @@
|
|
| 9 |
const https = require("https");
|
| 10 |
const http = require("http");
|
| 11 |
|
| 12 |
-
let PROXY_URL = process.env.CLOUDFLARE_PROXY_URL
|
| 13 |
-
if (
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
PROXY_URL = `https://${PROXY_URL}`;
|
| 15 |
}
|
| 16 |
|
| 17 |
-
const DEBUG = process.env.CLOUDFLARE_PROXY_DEBUG === "true"
|
| 18 |
|
| 19 |
// Allow user to define what to proxy. Use "*" to proxy everything except internal HF traffic.
|
| 20 |
-
const PROXY_DOMAINS =
|
| 21 |
-
|
|
|
|
|
|
|
| 22 |
const PROXY_ALL = PROXY_DOMAINS === "*";
|
| 23 |
|
| 24 |
if (PROXY_URL) {
|
|
@@ -43,53 +49,64 @@ if (PROXY_URL) {
|
|
| 43 |
path = options.pathname + options.search;
|
| 44 |
headers = options.headers || {};
|
| 45 |
} else {
|
| 46 |
-
hostname =
|
|
|
|
|
|
|
| 47 |
path = options.path || "/";
|
| 48 |
headers = options.headers || {};
|
| 49 |
}
|
| 50 |
|
| 51 |
// 2. Check if we should intercept (and prevent recursion)
|
| 52 |
-
const isInternal =
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
|
|
|
| 57 |
|
| 58 |
let shouldProxy = false;
|
| 59 |
if (PROXY_ALL) {
|
| 60 |
shouldProxy = !isInternal;
|
| 61 |
} else {
|
| 62 |
-
shouldProxy = BLOCKED_DOMAINS.some(
|
|
|
|
|
|
|
| 63 |
}
|
| 64 |
|
| 65 |
-
const alreadyProxied =
|
|
|
|
| 66 |
|
| 67 |
if (shouldProxy && !alreadyProxied) {
|
| 68 |
-
if (DEBUG)
|
|
|
|
|
|
|
|
|
|
| 69 |
|
| 70 |
// 3. Create fresh options for the proxied request
|
| 71 |
-
const newOptions =
|
| 72 |
-
|
| 73 |
-
|
|
|
|
| 74 |
|
| 75 |
// Ensure it's an object we can modify
|
| 76 |
-
if (typeof newOptions !== "object")
|
|
|
|
| 77 |
|
| 78 |
newOptions._proxied = true;
|
| 79 |
newOptions.protocol = "https:";
|
| 80 |
newOptions.hostname = proxy.hostname;
|
| 81 |
newOptions.port = proxy.port || 443;
|
| 82 |
-
|
| 83 |
// CRITICAL: Force fresh TLS handshake for the new domain
|
| 84 |
newOptions.servername = proxy.hostname;
|
| 85 |
-
delete newOptions.host;
|
| 86 |
delete newOptions.agent; // Force a new agent to prevent connection reuse issues
|
| 87 |
|
| 88 |
// Merge and update headers
|
| 89 |
newOptions.headers = {
|
| 90 |
...(newOptions.headers || {}),
|
| 91 |
-
|
| 92 |
-
"x-target-host": hostname
|
| 93 |
};
|
| 94 |
|
| 95 |
// Always use HTTPS for the proxy connection
|
|
@@ -105,13 +122,18 @@ if (PROXY_URL) {
|
|
| 105 |
|
| 106 |
if (DEBUG) {
|
| 107 |
if (PROXY_ALL) {
|
| 108 |
-
console.log(
|
|
|
|
|
|
|
| 109 |
} else {
|
| 110 |
-
console.log(
|
|
|
|
|
|
|
| 111 |
}
|
| 112 |
console.log(`[cloudflare-proxy] Target proxy: ${proxy.hostname}`);
|
| 113 |
}
|
| 114 |
} catch (e) {
|
| 115 |
-
if (DEBUG)
|
|
|
|
| 116 |
}
|
| 117 |
}
|
|
|
|
| 1 |
/**
|
| 2 |
* Cloudflare Proxy: Transparent Fix for Blocked Domains
|
| 3 |
+
*
|
| 4 |
* Patches https.request to redirect traffic for Telegram/Discord
|
| 5 |
* through a Cloudflare Worker proxy.
|
| 6 |
*/
|
|
|
|
| 9 |
const https = require("https");
|
| 10 |
const http = require("http");
|
| 11 |
|
| 12 |
+
let PROXY_URL = process.env.CLOUDFLARE_PROXY_URL;
|
| 13 |
+
if (
|
| 14 |
+
PROXY_URL &&
|
| 15 |
+
!PROXY_URL.startsWith("http://") &&
|
| 16 |
+
!PROXY_URL.startsWith("https://")
|
| 17 |
+
) {
|
| 18 |
PROXY_URL = `https://${PROXY_URL}`;
|
| 19 |
}
|
| 20 |
|
| 21 |
+
const DEBUG = process.env.CLOUDFLARE_PROXY_DEBUG === "true";
|
| 22 |
|
| 23 |
// Allow user to define what to proxy. Use "*" to proxy everything except internal HF traffic.
|
| 24 |
+
const PROXY_DOMAINS =
|
| 25 |
+
process.env.CLOUDFLARE_PROXY_DOMAINS ||
|
| 26 |
+
"api.telegram.org,discord.com,discordapp.com,gateway.discord.gg,status.discord.com";
|
| 27 |
+
const BLOCKED_DOMAINS = PROXY_DOMAINS.split(",").map((d) => d.trim());
|
| 28 |
const PROXY_ALL = PROXY_DOMAINS === "*";
|
| 29 |
|
| 30 |
if (PROXY_URL) {
|
|
|
|
| 49 |
path = options.pathname + options.search;
|
| 50 |
headers = options.headers || {};
|
| 51 |
} else {
|
| 52 |
+
hostname =
|
| 53 |
+
options.hostname ||
|
| 54 |
+
(options.host ? options.host.split(":")[0] : "");
|
| 55 |
path = options.path || "/";
|
| 56 |
headers = options.headers || {};
|
| 57 |
}
|
| 58 |
|
| 59 |
// 2. Check if we should intercept (and prevent recursion)
|
| 60 |
+
const isInternal =
|
| 61 |
+
hostname === "localhost" ||
|
| 62 |
+
hostname === "127.0.0.1" ||
|
| 63 |
+
hostname.endsWith(".hf.space") ||
|
| 64 |
+
hostname.endsWith(".huggingface.co") ||
|
| 65 |
+
hostname === "huggingface.co";
|
| 66 |
|
| 67 |
let shouldProxy = false;
|
| 68 |
if (PROXY_ALL) {
|
| 69 |
shouldProxy = !isInternal;
|
| 70 |
} else {
|
| 71 |
+
shouldProxy = BLOCKED_DOMAINS.some(
|
| 72 |
+
(domain) => hostname === domain || hostname.endsWith("." + domain),
|
| 73 |
+
);
|
| 74 |
}
|
| 75 |
|
| 76 |
+
const alreadyProxied =
|
| 77 |
+
options._proxied || (headers && headers["x-target-host"]);
|
| 78 |
|
| 79 |
if (shouldProxy && !alreadyProxied) {
|
| 80 |
+
if (DEBUG)
|
| 81 |
+
console.log(
|
| 82 |
+
`[cloudflare-proxy] Redirecting ${hostname}${path} -> ${proxy.hostname}`,
|
| 83 |
+
);
|
| 84 |
|
| 85 |
// 3. Create fresh options for the proxied request
|
| 86 |
+
const newOptions =
|
| 87 |
+
typeof options === "string" || options instanceof URL
|
| 88 |
+
? { protocol: "https:", path: path }
|
| 89 |
+
: { ...options };
|
| 90 |
|
| 91 |
// Ensure it's an object we can modify
|
| 92 |
+
if (typeof newOptions !== "object")
|
| 93 |
+
return original.apply(this, arguments);
|
| 94 |
|
| 95 |
newOptions._proxied = true;
|
| 96 |
newOptions.protocol = "https:";
|
| 97 |
newOptions.hostname = proxy.hostname;
|
| 98 |
newOptions.port = proxy.port || 443;
|
| 99 |
+
|
| 100 |
// CRITICAL: Force fresh TLS handshake for the new domain
|
| 101 |
newOptions.servername = proxy.hostname;
|
| 102 |
+
delete newOptions.host; // Prefer hostname
|
| 103 |
delete newOptions.agent; // Force a new agent to prevent connection reuse issues
|
| 104 |
|
| 105 |
// Merge and update headers
|
| 106 |
newOptions.headers = {
|
| 107 |
...(newOptions.headers || {}),
|
| 108 |
+
host: proxy.host,
|
| 109 |
+
"x-target-host": hostname,
|
| 110 |
};
|
| 111 |
|
| 112 |
// Always use HTTPS for the proxy connection
|
|
|
|
| 122 |
|
| 123 |
if (DEBUG) {
|
| 124 |
if (PROXY_ALL) {
|
| 125 |
+
console.log(
|
| 126 |
+
`[cloudflare-proxy] Transparent proxy active in WILDCARD mode (Proxying ALL except HF internal)`,
|
| 127 |
+
);
|
| 128 |
} else {
|
| 129 |
+
console.log(
|
| 130 |
+
`[cloudflare-proxy] Transparent proxy active for: ${BLOCKED_DOMAINS.join(", ")}`,
|
| 131 |
+
);
|
| 132 |
}
|
| 133 |
console.log(`[cloudflare-proxy] Target proxy: ${proxy.hostname}`);
|
| 134 |
}
|
| 135 |
} catch (e) {
|
| 136 |
+
if (DEBUG)
|
| 137 |
+
console.error(`[cloudflare-proxy] Failed to initialize: ${e.message}`);
|
| 138 |
}
|
| 139 |
}
|
cloudflare-worker.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
| 1 |
/**
|
| 2 |
* Cloudflare Worker: Universal Outbound Proxy
|
| 3 |
-
*
|
| 4 |
* Deployment:
|
| 5 |
* 1. Go to dash.cloudflare.com -> Workers & Pages -> Create Worker.
|
| 6 |
* 2. Paste this code and deploy.
|
| 7 |
* 3. Use your worker URL (e.g., https://my-proxy.workers.dev) as CLOUDFLARE_PROXY_URL.
|
| 8 |
-
*
|
| 9 |
* This worker reads the 'x-target-host' header to determine where to forward the request.
|
| 10 |
*/
|
| 11 |
|
|
@@ -13,7 +13,7 @@ export default {
|
|
| 13 |
async fetch(request, env, ctx) {
|
| 14 |
const url = new URL(request.url);
|
| 15 |
const targetHost = request.headers.get("x-target-host");
|
| 16 |
-
|
| 17 |
let targetBase = "";
|
| 18 |
|
| 19 |
if (targetHost) {
|
|
@@ -23,15 +23,21 @@ export default {
|
|
| 23 |
// Fallback: Guess based on path (legacy support)
|
| 24 |
if (url.pathname.startsWith("/bot")) {
|
| 25 |
targetBase = "https://api.telegram.org";
|
| 26 |
-
} else if (
|
|
|
|
|
|
|
|
|
|
| 27 |
targetBase = "https://discord.com";
|
| 28 |
} else {
|
| 29 |
-
return new Response(
|
|
|
|
|
|
|
|
|
|
| 30 |
}
|
| 31 |
}
|
| 32 |
|
| 33 |
const targetUrl = targetBase + url.pathname + url.search;
|
| 34 |
-
|
| 35 |
// Copy headers and remove internal/Cloudflare-specific ones
|
| 36 |
const headers = new Headers(request.headers);
|
| 37 |
headers.delete("cf-connecting-ip");
|
|
@@ -49,10 +55,10 @@ export default {
|
|
| 49 |
|
| 50 |
try {
|
| 51 |
const response = await fetch(modifiedRequest);
|
| 52 |
-
|
| 53 |
// Special handling for Discord/Telegram which might return 403 on some CF IPs
|
| 54 |
// If needed, you can add retry logic here.
|
| 55 |
-
|
| 56 |
return response;
|
| 57 |
} catch (e) {
|
| 58 |
return new Response(`Proxy Error: ${e.message}`, { status: 502 });
|
|
|
|
| 1 |
/**
|
| 2 |
* Cloudflare Worker: Universal Outbound Proxy
|
| 3 |
+
*
|
| 4 |
* Deployment:
|
| 5 |
* 1. Go to dash.cloudflare.com -> Workers & Pages -> Create Worker.
|
| 6 |
* 2. Paste this code and deploy.
|
| 7 |
* 3. Use your worker URL (e.g., https://my-proxy.workers.dev) as CLOUDFLARE_PROXY_URL.
|
| 8 |
+
*
|
| 9 |
* This worker reads the 'x-target-host' header to determine where to forward the request.
|
| 10 |
*/
|
| 11 |
|
|
|
|
| 13 |
async fetch(request, env, ctx) {
|
| 14 |
const url = new URL(request.url);
|
| 15 |
const targetHost = request.headers.get("x-target-host");
|
| 16 |
+
|
| 17 |
let targetBase = "";
|
| 18 |
|
| 19 |
if (targetHost) {
|
|
|
|
| 23 |
// Fallback: Guess based on path (legacy support)
|
| 24 |
if (url.pathname.startsWith("/bot")) {
|
| 25 |
targetBase = "https://api.telegram.org";
|
| 26 |
+
} else if (
|
| 27 |
+
url.pathname.startsWith("/api/webhooks") ||
|
| 28 |
+
url.pathname.startsWith("/api/v")
|
| 29 |
+
) {
|
| 30 |
targetBase = "https://discord.com";
|
| 31 |
} else {
|
| 32 |
+
return new Response(
|
| 33 |
+
"Invalid request. 'x-target-host' header missing and target not recognized via path.",
|
| 34 |
+
{ status: 400 },
|
| 35 |
+
);
|
| 36 |
}
|
| 37 |
}
|
| 38 |
|
| 39 |
const targetUrl = targetBase + url.pathname + url.search;
|
| 40 |
+
|
| 41 |
// Copy headers and remove internal/Cloudflare-specific ones
|
| 42 |
const headers = new Headers(request.headers);
|
| 43 |
headers.delete("cf-connecting-ip");
|
|
|
|
| 55 |
|
| 56 |
try {
|
| 57 |
const response = await fetch(modifiedRequest);
|
| 58 |
+
|
| 59 |
// Special handling for Discord/Telegram which might return 403 on some CF IPs
|
| 60 |
// If needed, you can add retry logic here.
|
| 61 |
+
|
| 62 |
return response;
|
| 63 |
} catch (e) {
|
| 64 |
return new Response(`Proxy Error: ${e.message}`, { status: 502 });
|
dns-fix.js
CHANGED
|
@@ -42,7 +42,9 @@ function dohResolve(hostname, callback) {
|
|
| 42 |
res.on("end", () => {
|
| 43 |
try {
|
| 44 |
if (res.statusCode !== 200) {
|
| 45 |
-
return callback(
|
|
|
|
|
|
|
| 46 |
}
|
| 47 |
const data = JSON.parse(body);
|
| 48 |
const aRecords = (data.Answer || []).filter((a) => a.type === 1);
|
|
@@ -58,7 +60,9 @@ function dohResolve(hostname, callback) {
|
|
| 58 |
}
|
| 59 |
});
|
| 60 |
});
|
| 61 |
-
req.on("error", (e) =>
|
|
|
|
|
|
|
| 62 |
req.on("timeout", () => {
|
| 63 |
req.destroy();
|
| 64 |
callback(new Error("DoH request timed out"));
|
|
|
|
| 42 |
res.on("end", () => {
|
| 43 |
try {
|
| 44 |
if (res.statusCode !== 200) {
|
| 45 |
+
return callback(
|
| 46 |
+
new Error(`DoH: server returned status ${res.statusCode}`),
|
| 47 |
+
);
|
| 48 |
}
|
| 49 |
const data = JSON.parse(body);
|
| 50 |
const aRecords = (data.Answer || []).filter((a) => a.type === 1);
|
|
|
|
| 60 |
}
|
| 61 |
});
|
| 62 |
});
|
| 63 |
+
req.on("error", (e) =>
|
| 64 |
+
callback(new Error(`DoH request failed: ${e.message}`)),
|
| 65 |
+
);
|
| 66 |
req.on("timeout", () => {
|
| 67 |
req.destroy();
|
| 68 |
callback(new Error("DoH request timed out"));
|