/*
* مثلاً فیلتر نیست (Masalan Filter Nist) - Web Proxy
* Copyright (C) 2026
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
const proxyOrigin = url.origin;
// صفحه اصلی - فرم ورود URL
if (url.pathname === "/" && !url.searchParams.has("url")) {
return new Response(getHomePage(), {
headers: { "Content-Type": "text/html; charset=utf-8" },
});
}
// دریافت URL مقصد
let targetUrl;
if (url.searchParams.has("url")) {
targetUrl = url.searchParams.get("url").trim();
} else {
// مسیر به صورت /https://example.com/path
// decode کردن کامل pathname و search به صورت جداگانه
let pathUrl = url.pathname.slice(1);
// decode کردن pathname
if (pathUrl.includes('%')) {
try {
// decode کردن چند مرحلهای برای مواقعی که double-encoded است
let decoded = decodeURIComponent(pathUrl);
// چک کنیم اگر هنوز % دارد، یک بار دیگر decode کن
if (decoded.includes('%')) {
try {
decoded = decodeURIComponent(decoded);
} catch (e) {
// اگر نشد، همان decoded اول را نگه دار
}
}
pathUrl = decoded;
} catch (e) {
// اگر decode نشد، همان را استفاده کن
pathUrl = url.pathname.slice(1);
}
}
// اضافه کردن search (که خودش decode نشده است)
if (url.search) {
pathUrl += url.search;
}
if (pathUrl.startsWith("http://") || pathUrl.startsWith("https://")) {
targetUrl = pathUrl;
} else {
// مسیر نسبی است - باید از Referer استفاده کنیم
const referer = request.headers.get("Referer");
if (referer && referer.includes(proxyOrigin)) {
try {
// استخراج کامل URL از referer
// مثال: https://proxy.com/https://www.youtube.com/watch -> https://www.youtube.com/watch
const refPath = new URL(referer).pathname.slice(1);
let refTargetUrl;
if (refPath.startsWith("http://") || refPath.startsWith("https://")) {
refTargetUrl = refPath;
} else {
// اگر referer هم مسیر نسبی داره، نمیتونیم resolve کنیم
return new Response(getHomePage(), {
headers: { "Content-Type": "text/html; charset=utf-8" },
});
}
// حالا pathUrl رو نسبت به refTargetUrl resolve میکنیم
const refTargetObj = new URL(refTargetUrl);
if (pathUrl.startsWith("/")) {
// مسیر مطلق - فقط origin را استفاده کن
targetUrl = refTargetObj.origin + pathUrl;
} else {
// مسیر نسبی - نسبت به URL فعلی
const refTargetPath = refTargetObj.pathname;
const refTargetDir = refTargetPath.substring(0, refTargetPath.lastIndexOf('/') + 1);
targetUrl = refTargetObj.origin + refTargetDir + pathUrl;
}
} catch (e) {
return new Response(getHomePage(), {
headers: { "Content-Type": "text/html; charset=utf-8" },
});
}
} else {
return new Response(getHomePage(), {
headers: { "Content-Type": "text/html; charset=utf-8" },
});
}
}
}
// اضافه کردن پروتکل اگر کاربر ننوشته
if (!targetUrl.startsWith("http://") && !targetUrl.startsWith("https://")) {
targetUrl = "https://" + targetUrl;
}
try {
const target = new URL(targetUrl);
// لیست User-Agent های جدید و واقعی (2025-2026)
const userAgents = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 14_3) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0"
];
// ساخت هدرهای واقعیتر
const headers = new Headers();
// استفاده از User-Agent کاربر یا انتخاب تصادفی
const clientUA = request.headers.get("User-Agent");
const userAgent = clientUA && clientUA.includes("Mozilla") ? clientUA : userAgents[Math.floor(Math.random() * userAgents.length)];
const isFirefox = userAgent.includes("Firefox");
const isSafari = userAgent.includes("Safari") && !userAgent.includes("Chrome");
// مدیریت Referer - استخراج یکبار
const originalReferer = request.headers.get("Referer");
let realReferer = target.origin + "/";
let isFromProxy = false;
if (originalReferer && originalReferer.includes(proxyOrigin)) {
isFromProxy = true;
const refMatch = originalReferer.match(/https?:\/\/[^/]+\/+(https?:\/\/.+)/);
if (refMatch) {
realReferer = refMatch[1];
}
}
// هدرهای اصلی
headers.set("User-Agent", userAgent);
headers.set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7");
headers.set("Accept-Language", "en-US,en;q=0.9");
headers.set("Accept-Encoding", "gzip, deflate, br, zstd");
headers.set("Upgrade-Insecure-Requests", "1");
// Cache-Control فقط برای GET
if (request.method === "GET") {
headers.set("Cache-Control", "max-age=0");
}
// Referer همیشه تنظیم میشود
headers.set("Referer", realReferer);
// Origin فقط برای POST/PUT
if (request.method === "POST" || request.method === "PUT") {
headers.set("Origin", target.origin);
}
// هدرهای مخصوص Chrome
if (!isFirefox && !isSafari) {
headers.set("Sec-Ch-Ua", '"Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"');
headers.set("Sec-Ch-Ua-Mobile", "?0");
headers.set("Sec-Ch-Ua-Platform", '"Windows"');
}
// Sec-Fetch headers - منطق درست
if (request.method === "POST" || request.method === "PUT") {
// چک میکنیم آیا این یک form submission است یا AJAX
const contentType = request.headers.get("Content-Type") || "";
const isFormSubmit = contentType.includes("application/x-www-form-urlencoded") || contentType.includes("multipart/form-data");
if (isFormSubmit) {
// Form submission
headers.set("Sec-Fetch-Dest", "document");
headers.set("Sec-Fetch-Mode", "navigate");
} else {
// AJAX request
headers.set("Sec-Fetch-Dest", "empty");
headers.set("Sec-Fetch-Mode", "cors");
}
headers.set("Sec-Fetch-Site", "same-origin");
headers.set("Sec-Fetch-User", "?1");
} else {
headers.set("Sec-Fetch-Dest", "document");
headers.set("Sec-Fetch-Mode", "navigate");
if (isFromProxy) {
// چک کردن اگر referer از همان host است
try {
const refUrl = new URL(realReferer);
if (refUrl.host === target.host) {
headers.set("Sec-Fetch-Site", "same-origin");
} else {
headers.set("Sec-Fetch-Site", "cross-site");
}
} catch {
headers.set("Sec-Fetch-Site", "same-origin");
}
} else {
headers.set("Sec-Fetch-Site", "none");
}
headers.set("Sec-Fetch-User", "?1");
}
// فوروارد تمام کوکیها - مدیریت بهتر
const cookies = request.headers.get("Cookie");
if (cookies) {
// تمیز کردن و فوروارد کوکیها
const cleanCookies = cookies
.split(';')
.map(c => c.trim())
.filter(c => c.length > 0)
.join('; ');
if (cleanCookies) {
headers.set("Cookie", cleanCookies);
}
}
// Content-Type برای POST/PUT
if (request.method === "POST" || request.method === "PUT") {
const contentType = request.headers.get("Content-Type");
if (contentType) {
headers.set("Content-Type", contentType);
}
}
// بدون تاخیر - تاخیر مصنوعی باعث timeout و مشکلات دیگر میشود
// درخواست به سایت مقصد
const response = await fetch(target.toString(), {
method: request.method,
headers: headers,
body: request.method !== "GET" && request.method !== "HEAD" ? request.body : undefined,
redirect: "manual",
});
// مدیریت ریدایرکت
if (response.status >= 300 && response.status < 400) {
const location = response.headers.get("Location");
if (location) {
// حل کردن URL نسبی به مطلق
let absoluteLocation;
try {
// اگر location یک URL کامل است
if (location.startsWith('http://') || location.startsWith('https://')) {
absoluteLocation = location;
} else if (location.startsWith('//')) {
// پروتکل نسبی
absoluteLocation = target.protocol + location;
} else if (location.startsWith('/')) {
// مسیر مطلق - باید نسبت به target origin حل شود
absoluteLocation = target.origin + location;
} else if (location.startsWith('?')) {
// Query string فقط - به pathname فعلی اضافه میشود
absoluteLocation = target.origin + target.pathname + location;
} else if (location.startsWith('#')) {
// Fragment فقط
absoluteLocation = target.origin + target.pathname + target.search + location;
} else {
// مسیر نسبی - باید نسبت به pathname فعلی حل شود
const targetPath = target.pathname.substring(0, target.pathname.lastIndexOf('/') + 1);
absoluteLocation = target.origin + targetPath + location;
}
} catch (e) {
// در صورت خطا، استفاده از روش قدیمی
absoluteLocation = new URL(location, target).toString();
}
const newHeaders = new Headers(response.headers);
newHeaders.set("Location", `${proxyOrigin}/${absoluteLocation}`);
// حفظ کوکیها در ریدایرکت
const setCookies = [];
response.headers.forEach((value, key) => {
if (key.toLowerCase() === 'set-cookie') {
setCookies.push(value);
}
});
if (setCookies.length > 0) {
newHeaders.delete('set-cookie');
setCookies.forEach(cookie => {
let modifiedCookie = cookie
.replace(/;\s*domain=[^;]*/gi, '')
.replace(/;\s*secure\s*(?=;|$)/gi, '')
.replace(/;\s*samesite=strict/gi, '; SameSite=None')
.replace(/;\s*samesite=lax/gi, '; SameSite=None');
if (!modifiedCookie.toLowerCase().includes('samesite=')) {
modifiedCookie += '; SameSite=None';
}
newHeaders.append('Set-Cookie', modifiedCookie);
});
}
return new Response(null, {
status: response.status,
headers: newHeaders,
});
}
}
const contentType = response.headers.get("Content-Type") || "";
// بازنویسی HTML
if (contentType.includes("text/html")) {
let html = await response.text();
html = rewriteHtml(html, proxyOrigin, target);
const newHeaders = new Headers(response.headers);
newHeaders.delete("content-encoding");
newHeaders.delete("content-length");
newHeaders.delete("content-security-policy");
newHeaders.delete("content-security-policy-report-only");
newHeaders.delete("x-frame-options");
newHeaders.delete("strict-transport-security");
newHeaders.set("Access-Control-Allow-Origin", "*");
newHeaders.set("Access-Control-Allow-Credentials", "true");
// مدیریت بهتر کوکیها
const setCookies = [];
response.headers.forEach((value, key) => {
if (key.toLowerCase() === 'set-cookie') {
setCookies.push(value);
}
});
if (setCookies.length > 0) {
newHeaders.delete('set-cookie');
setCookies.forEach(cookie => {
let modifiedCookie = cookie
.replace(/;\s*domain=[^;]*/gi, '')
.replace(/;\s*secure\s*(?=;|$)/gi, '')
.replace(/;\s*samesite=strict/gi, '; SameSite=None')
.replace(/;\s*samesite=lax/gi, '; SameSite=None');
if (!modifiedCookie.toLowerCase().includes('samesite=')) {
modifiedCookie += '; SameSite=None';
}
newHeaders.append('Set-Cookie', modifiedCookie);
});
}
return new Response(html, {
status: response.status,
headers: newHeaders,
});
}
// بازنویسی CSS
if (contentType.includes("text/css")) {
let css = await response.text();
css = rewriteCss(css, proxyOrigin, target);
const newHeaders = new Headers(response.headers);
newHeaders.delete("content-encoding");
newHeaders.delete("content-length");
newHeaders.set("Access-Control-Allow-Origin", "*");
return new Response(css, {
status: response.status,
headers: newHeaders,
});
}
// بازنویسی JavaScript
if (contentType.includes("application/javascript") || contentType.includes("text/javascript")) {
let js = await response.text();
js = rewriteJs(js, proxyOrigin, target);
const newHeaders = new Headers(response.headers);
newHeaders.delete("content-encoding");
newHeaders.delete("content-length");
newHeaders.set("Access-Control-Allow-Origin", "*");
return new Response(js, {
status: response.status,
headers: newHeaders,
});
}
// سایر محتواها بدون تغییر
const newHeaders = new Headers(response.headers);
newHeaders.set("Access-Control-Allow-Origin", "*");
newHeaders.delete("content-security-policy");
newHeaders.delete("content-security-policy-report-only");
return new Response(response.body, {
status: response.status,
headers: newHeaders,
});
} catch (error) {
console.error('Proxy Error:', error);
return new Response(getErrorPage(error.message || 'خطای نامشخص'), {
status: 500,
headers: { "Content-Type": "text/html; charset=utf-8" },
});
}
}
};
function getHomePage() {
return `
مثلاً فیلتر نیست
🔥
مثلاً فیلتر نیست
به هر سایتی مثلا دسترسی داشته باشید یا جستجو کنید - سریع و امن
مثلا داکیومنتهای برنامهنویسی
مثلا سایتهای محبوب
`;
}
function getErrorPage(message) {
return `
خطا - وب پراکسی
🚨
مشکلی پیش آمد
${escapeHtml(message)}
← بازگشت به صفحه اصلی
راهنما:
- ✓ آدرس سایت را بررسی کنید
- ✓ از صحیح بودن نام دامنه مطمئن شوید
- ✓ ممکن است سایت موقتاً در دسترس نباشد
- ✓ برای Google از DuckDuckGo استفاده کنید
`;
}
function escapeHtml(text) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
return text.replace(/[&<>"']/g, m => map[m]);
}
function rewriteHtml(html, proxyOrigin, targetUrl) {
const targetOrigin = targetUrl.origin;
const targetHost = targetUrl.host;
// بازنویسی base tag اگر وجود دارد
html = html.replace(/]+)href=(["'])([^"']+)(["'])([^>]*)>/gi, (match, before, q1, href, q2, after) => {
let newHref = href;
if (href.startsWith('http://') || href.startsWith('https://')) {
if (!href.startsWith(proxyOrigin)) {
newHref = `${proxyOrigin}/${href}`;
}
} else if (href.startsWith('//')) {
newHref = `${proxyOrigin}/https:${href}`;
} else if (href.startsWith('/')) {
newHref = `${proxyOrigin}/${targetOrigin}${href}`;
}
return ``;
});
// بازنویسی لینکهای کامل با http/https
html = html.replace(/(href|src|action|data|poster|background)=(["'])(https?:\/\/[^"']+)(["'])/gi, (match, attr, q1, url, q2) => {
return `${attr}=${q1}${proxyOrigin}/${url}${q2}`;
});
// بازنویسی لینکهای نسبی به پروتکل (//)
html = html.replace(/(href|src|action|data|poster)=(["'])(\/\/[^"']+)(["'])/gi, (match, attr, q1, url, q2) => {
return `${attr}=${q1}${proxyOrigin}/https:${url}${q2}`;
});
// بازنویسی لینکهای نسبی (/)
html = html.replace(/(href|src|action|data|poster|background)=(["'])(\/[^/"'][^"']*)(["'])/gi, (match, attr, q1, path, q2) => {
if (path.startsWith("//")) return match;
return `${attr}=${q1}${proxyOrigin}/${targetOrigin}${path}${q2}`;
});
// بازنویسی meta refresh
html = html.replace(/]*http-equiv=["']refresh["'][^>]*content=["'][^"']*url=)([^"']+)(["'][^>]*)>/gi, (match, before, url, after) => {
let newUrl = url.trim();
if (newUrl.startsWith('http://') || newUrl.startsWith('https://')) {
newUrl = `${proxyOrigin}/${newUrl}`;
} else if (newUrl.startsWith('//')) {
newUrl = `${proxyOrigin}/https:${newUrl}`;
} else if (newUrl.startsWith('/')) {
newUrl = `${proxyOrigin}/${targetOrigin}${newUrl}`;
}
return ``;
});
// تزریق اسکریپت برای مدیریت لینکهای داینامیک
const script = ``;
// اضافه کردن base tag برای مدیریت بهتر URL های نسبی
const baseTag = ``;
// تزریق base و script به head
if (html.match(/]*>/i)) {
html = html.replace(/]*>/i, (match) => match + "\n" + baseTag + "\n" + script);
} else if (html.includes('')) {
html = html.replace(/<\/head>/i, baseTag + "\n" + script + "\n");
} else {
// اگر head نبود، قبل از body
html = html.replace(/ {
return `url(${q1}${proxyOrigin}/${url}${q2})`;
});
// بازنویسی url() با //
css = css.replace(/url\((["']?)(\/\/[^)"']+)(["']?)\)/gi, (match, q1, url, q2) => {
return `url(${q1}${proxyOrigin}/https:${url}${q2})`;
});
// بازنویسی url() با /
css = css.replace(/url\((["']?)(\/[^)"']+)(["']?)\)/gi, (match, q1, path, q2) => {
if (path.startsWith("//")) return match;
return `url(${q1}${proxyOrigin}/${targetOrigin}${path}${q2})`;
});
// بازنویسی @import
css = css.replace(/@import\s+(["'])(https?:\/\/[^"']+)(["'])/gi, (match, q1, url, q2) => {
return `@import ${q1}${proxyOrigin}/${url}${q2}`;
});
return css;
}
function rewriteJs(js, proxyOrigin, targetUrl) {
const targetOrigin = targetUrl.origin;
// بازنویسی محافظهکارانه URLها در JavaScript
try {
// بازنویسی URLهای با کوتیشن دوتایی
js = js.replace(/"(https?:\/\/[^"]+)"/g, (match, url) => {
if (url.startsWith(proxyOrigin)) return match;
return `"${proxyOrigin}/${url}"`;
});
// بازنویسی URLهای با کوتیشن تکی
js = js.replace(/'(https?:\/\/[^']+)'/g, (match, url) => {
if (url.startsWith(proxyOrigin)) return match;
return `'${proxyOrigin}/${url}'`;
});
// بازنویسی URLهای با backtick
js = js.replace(/`(https?:\/\/[^`]+)`/g, (match, url) => {
if (url.startsWith(proxyOrigin)) return match;
return `\`${proxyOrigin}/${url}\``;
});
} catch (e) {
// در صورت خطا، JavaScript را بدون تغییر برگردان
console.error('Error rewriting JS:', e);
}
return js;
}