/* * مثلاً فیلتر نیست (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 ` مثلاً فیلتر نیست

مثلاً فیلتر نیست

به هر سایتی مثلا دسترسی داشته باشید یا جستجو کنید - سریع و امن

با http:// یا https:// برای سایت، بدون آن برای جستجو
مثلا داکیومنت‌های برنامه‌نویسی
مثلا سایت‌های محبوب
`; } 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; }