如何基于IP地址实现访客分向10个不同URL的纯JS方案?
问题
我希望将访问网站的每位用户定向至不同URL,最多设置10个目标地址。规则如下:
- 第1位访客访问
www.youarelovely.com时直接打开该站点 - 第2位访客重定向至
www.youarelovely.com/1 - 第3位访客重定向至
www.youarelovely.com/2 - ……以此类推至第10个地址
www.youarelovely.com/9
需求:
- 通过检测用户IP地址并存储映射关系,已有IP访客直接定向至对应URL,新IP分配下一个可用地址
- 仅用纯HTML和JavaScript实现,部署在GitHub Pages或Codepen,为概念验证项目,无需复杂实现
- 非地域重定向,所有访客地域相同
核心限制与解决方案
纯前端HTML/JS在静态托管环境(如GitHub Pages)下无法直接读写服务器文本文件,因为前端没有服务器文件系统权限。因此需要借助两个公共API实现核心功能:
- 免费IP查询API:获取用户公网IP
- 免费键值存储API:替代文本文件存储IP与路径的全局映射
以下是概念验证的实现代码:
实现代码
// 基础配置 const BASE_URL = 'https://www.youarelovely.com'; const MAX_ALLOCATIONS = 10; // 最多10个目标地址 // 替换为你的键值存储API端点(如Firebase Realtime Database、Supabase REST接口) const STORAGE_ENDPOINT = 'https://your-storage-service.example.com/ip-mappings'; // 获取用户公网IP async function getPublicIp() { try { const res = await fetch('https://ipapi.co/ip/'); if (!res.ok) throw new Error(`HTTP错误: ${res.status}`); return await res.text(); } catch (err) { console.error('获取IP失败:', err); // 失败时返回随机模拟ID,仅用于本地测试 return `test-${Math.random().toString(36).substring(2, 10)}`; } } // 查询IP对应的已分配路径 async function getAssignedPath(ip) { try { const res = await fetch(`${STORAGE_ENDPOINT}/${ip}`); if (res.ok) { const data = await res.json(); return data.path; } else if (res.status === 404) { return null; // IP未被分配 } throw new Error(`查询映射失败: ${res.status}`); } catch (err) { console.error('查询IP映射出错:', err); return null; } } // 为新IP分配路径并存储映射 async function allocateNewPath(ip) { try { // 获取所有已分配的映射,统计数量 const res = await fetch(STORAGE_ENDPOINT); if (!res.ok) throw new Error(`获取映射列表失败: ${res.status}`); const allMappings = await res.json(); const allocatedCount = Object.keys(allMappings).length; if (allocatedCount >= MAX_ALLOCATIONS) { // 已满10个分配,返回默认主站路径 return ''; } // 分配规则:第1个是''(主站),第2个是'1',...第10个是'9' const newPath = allocatedCount === 0 ? '' : `${allocatedCount - 1}`; // 存储新的IP-路径映射 await fetch(`${STORAGE_ENDPOINT}/${ip}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ path: newPath }) }); return newPath; } catch (err) { console.error('分配路径失败:', err); return ''; // 失败时返回主站 } } // 主重定向逻辑 async function handleRedirect() { const userIp = await getPublicIp(); let targetPath = await getAssignedPath(userIp); if (!targetPath) { targetPath = await allocateNewPath(userIp); } const targetUrl = targetPath ? `${BASE_URL}/${targetPath}` : BASE_URL; if (window.location.href !== targetUrl) { console.log(`重定向至: ${targetUrl}`); window.location.href = targetUrl; } else { console.log('已处于目标页面'); } } // 页面加载时执行 window.addEventListener('load', handleRedirect);
关键说明
- 存储API配置:你需要自己搭建或使用免费的键值存储服务(如Firebase Realtime Database),替换代码中的
STORAGE_ENDPOINT,这类服务提供的免费额度足够支撑概念验证。 - IP查询API备选:如果
ipapi.co请求受限,可替换为https://api.ipify.org/等其他免费IP查询服务。 - 本地模拟方案:若无法使用外部存储API,可改用
localStorage替代,但该方案仅在同一浏览器内有效,无法实现跨设备的全局IP分配。
内容的提问来源于stack exchange,提问作者Hither Joe
相关产品推荐
相关产品推荐

