推荐阅读:
在之前的文章都是围绕lucky的stun端口利用cloudflare的重定向国内来实现http直连进行展开,但是这样会有一些缺陷
缺点
1、会经历重定向过程
http访问需要进行重定向才可以访问到实际的域名和端口号,并且这个过程是比较就漫长的,相较于直接直接中转是不够快的
2、存在端口号,不够完美
以往的方案都是使用cloudflare的规则触发重定向到新的域名+端口来实现直连访问的,最后访问的页面都是带端口号的,这样会显的不够美观
3、部分APP类应用不适用
使用重定向功能需要依赖访问者的工具是否支持重定向功能,一般来说对于浏览器的话可以直接直接处理30X的重定向请求,但是对于一些APP例如Jellyfin、Emby、home assistant等是APP是不支持重定向的,尽管浏览器的网页访问方式不受影响,但对于对应的APP却无法正常连接了
新方案
cloudflare的workers功能最常用的就是用来进行流量代理,我们可以利用workers的js的数据功能来实现流量转发至lucky开放的stun穿透端口来实现完美http访问

本教程只使用了一个域名进行操作,因为反向代理后的域名对用户是不可知的,使用三级泛域名解析实际IP地址,域名长一点也无所谓
一、添加CNAME记录到IP优选域名
在DNS解析记录这里填入一个CNAME类型的*泛域名的解析记录,这里用cf.090227.xyz作为演示,大家也可以使用其他的优选域名。

二、获取ddns令牌
在cf->我->配置文件->API令牌来创建一个ddns令牌(已经有dns的API令牌可以跳过)


API令牌只会显示一次,记录保存下来

三、设置DDNS解析
回到lucky->动态域名->添加任务来添加你的ddns解析,解析到三级域名的泛解析*.p.xxx.com,最后实际回源访问lucky使用的就是三级域名来访问了

三、获取workers API令牌
同样在cf->我->配置文件->API令牌->自定义令牌来创建一个workers令牌(已有workers的API令牌可以跳过)

权限设置为 账户、workers脚本、编辑;包括所有账户
创建完成后保存workers令牌备用
四、添加proxy-workers
侧边栏->管理workers->创建->创建workers来创建一个workers

名称填proxy,点击部署

完成后点击编辑代码

粘贴下列代码块并修改配置参数,主要修改你的域名和端口号,点击部署
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
})
async function handleRequest(request) {
const sourceDomain = 'example.com';
const targetDomain = 'p.example.com';
const targetPort = '5234';
const protocol = 'https';
const url = new URL(request.url);
if (url.hostname.endsWith(sourceDomain)) {
// 直接把 hostname 替换
const newHost = url.hostname.replace(sourceDomain, targetDomain);
const targetUrl = `${protocol}://${newHost}:${targetPort}${url.pathname}${url.search}`;
const modifiedRequest = new Request(targetUrl, {
method: request.method,
headers: request.headers,
body: request.body,
redirect: 'manual',
});
return fetch(modifiedRequest);
}
return new Response('Unsupported domain.', { status: 403 });
}JavaScript点击部署即可完成配置
五、添加workers路由
在设置栏添加一个路由,把*.example.com/*进行路由,这样就可以搭配workers进行域名和路径的js重定向了


最后将这个wokers页面隐藏就能够完美重定向了,也可以不隐藏,在这个js脚本基础上再加一些别的信息显示,通过workers.dev的链接打开可以显示一些信息。预览url不要开启,是用来做调试用的

这样就可以实现通过*.example.com来访问到*.p.example.com:port了,并且对于访问者来说只会显示*.example.com,全程没有感觉进行了反向代理
这里其实可以不进行泛域名解析, 只要搭配多条路由规则,可以指定哪些域名走重定向直连,那些域名走反向代理。例如ha.a.com、emby.a.com需要走代理,就单独给proxy的workers添加ha.a.com和emby.a.com的路由规则,其他的都设置到redirect的路由规则*.a.com,反之亦然
六、设置stun自动同步更新端口号(重点)
添加一条穿透规则,设置webhook为如下规则:
- 接口地址:
https://api.cloudflare.com/client/v4/accounts/账户ID/workers/scripts/脚本名字 - 请求方法:
PUT - 请求头:
Authorization: Bearer workers的API密钥Content-Type: application/javascript
- 接口调用成功包含的字符串:
"success": true - 请求体:
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
})
async function handleRequest(request) {
const sourceDomain = '你的域名.com';
const targetDomain = 'p.你的域名.com';
const targetPort = '#{port}';
const protocol = 'https';
const url = new URL(request.url);
if (url.hostname.endsWith(sourceDomain)) {
// 直接把 hostname 替换
const newHost = url.hostname.replace(sourceDomain, targetDomain);
const targetUrl = `${protocol}://${newHost}:${targetPort}${url.pathname}${url.search}`;
const modifiedRequest = new Request(targetUrl, {
method: request.method,
headers: request.headers,
body: request.body,
redirect: 'manual',
});
return fetch(modifiedRequest);
}
return new Response('Unsupported domain.', { status: 403 });
}JavaScript配置好就完成自动更新端口号了

最后进入stun的请求域名都是以*.p.example.com来访问端口
后记
其实这个算是补偿重定向直连带端口号的缓解措施,因为毕竟全部流量都需要走cf中转,连接速度和延迟都挺大的,并且免费额度存在每日最大强请求次数为100k次,对于小数据高并发的服务用的比较勤快的话还是可以用完的免费额度的。最后实测下来个人觉得能走重定向直连的服务尽量走重定向直连方案,毕竟有时候加载速度慢的问题还是容易被感知到的,不过可以搭配cf的缓存服务或者CDN优选域名的方式来缓解访问速度慢的问题。
目前最主要的应用还是用于解决一些APP无法处理重定向请求的内容,不过对于一些APP本身支持重定向访问(例如:mtphoto、飞牛app)都是可以支持重定向直连,而不仅受限于网页访问或者使用反向代理访问,这种情况最好就是直接使用重定向直连的方式来访问了。如果是想消除端口号和对速度和延迟可以接受的用户来说使用反向代理的方式是更加美观的方式的











大佬可以出个视频教程吗,一些步骤不太熟练
目前视频的话还没有时间准备,你有什么不懂的话可以加我q:2546046421详细交流吧
https://club.fnnas.com/forum.php?mod=viewthread&tid=26190&page=1#pid125412,飞牛app使用重定向方案好像有点问题
这个问题是飞牛官方APP的问题,目前发布的版本还没有修复这个BUG,,但是我们内部和他们协商出来了一个测试版本,已经解决这个问题了,有需要的话可以转发给你试试
全部流量都需要走cf中转,那么直接CF隧道和优选不就好了?这里面的stun也就不起作用了啊
可以的,但是隧道是需要安装cloudflare自己的应用,这里使用lucky仅仅使用了cloudflare的转发而已
大佬,可以实现代理到lucky原有的域名吗 例如原来lucky反代了*.ccccc.com域名,可以实现通过*.example.com来访问*.ccccc.com:port吗?
可以的,只需要修改反代后域名就可以了
这个不行,
看了你的教程,自己研究测试了几天都实现不了这个,根据你的教程研究出了 指定前缀代理+302重定向根据V4V6跳转端口,
例如a.com、b.com两个域名
指定前缀:1、2
前缀1.a.com代理1.p.a.com:穿透后端口
前缀2.a.com代理2.p.a.com:穿透后端口
其它未指定的前缀 例如3、4
v4网络下:
3.a.com重定向至3.b.com:穿透后端口
4.a.com重定向至4.b.com:穿透后端口
v6网络下
3.a.com重定向至3.b.com:lucky指定的监听的V6端口
4.a.com重定向至4.b.com:lucky指定的监听的V6端口
就是1.a.com代理1.p.a.com:穿透后端口 可以,这个的就需要再lucky反代那里额外新增一下1.p.a.com
1.a.com代理1.b.com:穿透后端口 这个搞不定 ,这个搞定的话 原来lucky反代的b.com 都不需要修改,可以节省一个步骤,想代理的直接到stun设置webhook的请求体内加上代理前缀就行了
我不太明白为什么你要这么设置,lucky设置反向代理是必须的,并且对应的域名也应该为接入域名也应该为*.p.a.com,详细你可以查看lucky的反向代理日志,看其返回的域名是哪一个
另外,这个方案我只是写出来而已,实际上连我自己都不使用这篇文章的方案,因为测试出来发现效果并不理想
目前我全部网站所使用的方案都是CDN的方案:https://www.ytca.top/stun/2193/
我认为是目前最完美的方案,当然做为替代方案CF也是一种不错的选择,唯一缺点就是要一个备案域名
😂有点强迫症,瞎折腾。。。其实功能都一样
lucky动态域名设置的*.b.a.com
整个操作完成后可以使用c.b.a.com:port访问
访问c.a.com cloudflaer提示
Web server is returning an unknown error Error code 520
You
Browser Working
Los Angeles
Cloudflare Working
c.b.a.com
Host Error
worker的端口正常更新
日志
fetch
GET /favicon.icon
HTTP 520
1 Errors
GET https://b.a.com/favicon.ico
源站是有Let’s Encrypt ssl证书的,cf的ssl配置为灵活
关于这个问题无法回源的话八成的运营商封锁了cf,这个方案应该用不了,改用https://www.ytca.top/stun/2193/这个方案吧
我用来挂openlist的,用这个回源流量不太够吧
你openlist挂载本地路径吗?不挂载全部都是用网盘的话,这些网盘大部分都支持重定向连接,因此实际下载的时候不会消耗CDN流量,
记得CDN别开启重定向跟随就可以了
那没办法了,就是挂本地路径才搭在家里面的,挂云盘可以搭很多地方,还是换回规则有端口号就有吧。
如果使用openlist的话重定向方案就很够用了呀,这个免端口号的主要是面向APP类型等服务不支持重定向的缓解措施,既然是缓解措施,能够使用重定向直连定是直连最好呀
可能我觉得看着好看吧,现在也只能这样了。
你的优选域名、workers路由都配置没问题是吗?可以用这个来测试是否是是你的配置出问题,如果这样配置配置可以通过你的域名正常访问百度的页面的话,说明就是运营商封锁了cf
我在lucky的stun请求体改成了您说的
async function handleRequest(request) {
const sourceDomain = ‘c.a.com’;
const targetDomain = ‘www.baidu.com’;
const targetPort = ‘443’;
const protocol = ‘https’;
}
addEventListener(‘fetch’, event => {
event.respondWith(handleRequest(event.request));
});
也确实访问到了百度,我之前配置也复制的您上面的进行修改的,上面日志里worker也确实进行了转发,具体为什么就不太清楚
如果百度可以正常访问,那八成就是运营商封锁了cf的回源请求
昨晚成功无端口访问了,今天一弄又不行了,昨晚lucky日志只显示什么什么tcp4检测通过,今天的都是通关后立马断开
可能是你的路由器配置问题,导致lucky的stun通道无法开启,尝试换端口和不使用内置转发再尝试一下
大佬能加一下吗,我发现我不使用lucky内置转发自己端口转发可以,但是只能一个用服务,不是很懂lucky,求指导一下
q:2546046421
大佬,加你了
没有收到好友申请哦
可能加错了,没事我搞定了,谢谢博主的教程哈哈
希望可以出方案外网连接群晖的App,群晖的App目前貌似不支持重定向,连接不上。
这个只能依靠官方升级了,要么就只能使用内网穿透方案内外访问了
https://www.ytca.top/stun/2064/
大佬“最后将这个wokers页面隐藏就能够完美重定向了”这句话是什么意思?是要点一下图片上指示的禁用按钮吗?
是的,你不禁用也没关系
成功了但是unsurpported domain
哪里显示不支持域名
已成功,nas所有服务全部使用 v4/v6 自动分流的方式,通过worker脚本进行路由,同时针对ipv4-stun穿透的链接,使用worker代理转发(完全隐藏STUN随机端口号)(ipv6使用的固定端口号,就不转发了,直接重定向到v6.域名:固定端口)。 通过修改对应的列表,可以控制某些服务 直连或者 重定向 或者转发。 就是这样弄 worker免费的10万请求数量用的比较快。 特别是视频或者音乐服务器,会持续转发请求。 不过这种白嫖方案,已经很完美了。
另外修改了 lucky端口的推送方式, 通过CF接口,将动态端口推送到 worker的变量中,脚本通过获取变量值来动态应用stun端口。 代码中不暴露端口。
如果是使用重定向分流的话,就不建议v4代理回源了,拖慢访问速度。
建议v4和v6都使用重定向分流访问
要么就单独v4/v6代理回源就够了
做到v4和v6的访问行为一致,这样不需要考虑不同网络环境下的访问问题
另外,使用变量并没有相较于编写在js上有什么优势,因为只要把workers的直接访问uri禁用后,对外域名访问js内容本身就是不可见的,因此个人觉得变量方式没有显著提升的优势
如果是使用重定向分流的话,就不建议v4代理回源了,拖慢访问速度。
建议v4和v6都使用重定向分流访问
要么就单独v4/v6代理回源就够了
做到v4和v6的访问行为一致,这样不需要考虑不同网络环境下的访问问题
另外,使用变量并没有相较于编写在js上有什么优势,因为只要把workers的直接访问uri禁用后,对外域名访问js内容本身就是不可见的,因此个人觉得变量方式没有显著提升的优势
因为v4 使用stun之后 同一个前缀的服务 每次端口也不同,浏览器记住密码的策略会将不同端口视为不同网站,无法使用填充密码功能了,而v6直连的话端口是固定的。所以我分流之后,v6选择不代理。 v4stun的流量进行代理转发,隐藏端口。
如果可以,麻烦发布一下workers脚本内容出来,也许有人会用到这个分流功能
我用的代码如下:(自己根据用途改了一些。不需要的可以删除部分逻辑)
//worker分流脚本更新时间: 2026-01-28 22:55:00//根据客户端IP地址类型自动分流重定向地址,ipv4或者v6
//可选服务进行代理转发
//可选通过worker变量来更新端口号,好处是代码可以放在github仓库,通过worker拉取部署,github修改代码,自动重新部署。 变量通过lucky进行推送。脚本部署之后就可以不变了。
let CONFIG = {
sourceDomain: 'Domain', // Workers路由域名
ipv4TargetDomain: 'stun.Domain', // IPv4 STUN穿透域名
ipv4Address: '1.1.1.1', // IPv4 STUN穿透地址
ipv6TargetDomain: 'v6.Domain', // IPv6固定端口域名
ipv4Port: '2176', // IPv4动态端口
ipv6Port: '4444' // IPv6默认端口
};
// IPv6端口映射配置 固定配置不动态修改
let IPV6_PORT_MAP = {
'fn': '1234',
'lucky': '1234',
'sun': '1234',
'music': '1234',
'alist': '1234'
// 可以继续添加其他域名前缀和端口映射
};
// IPv4端口直连映射配置,对于直连的服务,可以直接使用ipv4地址进行访问,不进行反代。
let IPV4_PORT_MAP = {
'sun': '2303', //单独通过主路由一对一映射,不经过lucky代理
'music': '1234',
'music1': '1234',
'fn': 1234',
'fn1': '1234',
'alist': '1234',
'ha': '1234'
// 可以继续添加其他域名前缀和端口映射
};
// 需要重定向到HTTP的域名前缀列表(对IPv4和IPv6都生效)
const HTTP_PREFIXES = [
'alist',
'qlong',
'ha'
// 可以继续添加其他需要HTTP的前缀
];
// 需要代理转发(无端口访问)的域名前缀列表(仅对IPv4生效)
const Proxy_PREFIXES = [
'mi','xx','xx','fn','xx','ql','xx','xx',
'xx','xx','xx','xx'
// 可以继续添加其他需要代理转发的前缀
];
export default {
async fetch(request, env, ctx) { // env 作为参数传入
// parseEnvVar 函数需要调整,接收 env 作为第一个参数 从变量中读取参数
CONFIG = parseEnvVar(env, 'CONFIG', CONFIG);
IPV4_PORT_MAP = parseEnvVar(env, 'IPV4_PORT_MAP', IPV4_PORT_MAP);
IPV6_PORT_MAP = parseEnvVar(env, 'IPV6_PORT_MAP', IPV6_PORT_MAP);
return handleRequest(request);
}
};
async function handleRequest(request) {
const url = new URL(request.url);
const hostname = url.hostname;
// 提取域名前缀(去掉源域名部分)
const prefixMatch = hostname.match(new RegExp(
^(.*?)\\.${CONFIG.sourceDomain.replace(/\./g, '\\.')}$));if (!prefixMatch || !prefixMatch[1]) {
// 如果没有匹配到前缀,返回错误代码
return new Response('Not Found', { status: 404 });
}
const prefix = prefixMatch[1];
// 检查是否匹配源域名
if (!hostname.includes(CONFIG.sourceDomain)) {
return new Response('Not Found', { status: 404 });
}
// 检测客户端IP版本
const clientIP = request.headers.get('CF-Connecting-IP');
log(
访问服务IP: ${clientIP});const isIPv6 = clientIP && clientIP.includes(':');
let targetDomain, targetPort, protocol;
if (isIPv6) {
// IPv6客户端 -> 使用IPv6域名并根据前缀选择端口
targetDomain = CONFIG.ipv6TargetDomain;
log(
服务分流v6: ${targetDomain});// 根据域名前缀动态选择IPv6端口
targetPort = getIPv6PortByHostname(hostname);
} else {
// IPv4客户端 -> 使用STUN穿透域名和动态端口
// targetDomain = CONFIG.ipv4TargetDomain;
targetDomain = getIPv4Address(hostname); // 根据域名判断使用ipv4-stun域名,还是直接使用ip地址
log(
服务分流v4-Stun: ${targetDomain});// 根据域名前缀动态选择IPv4端口
targetPort = getIPv4PortByHostname(hostname);
}
// 根据targetDomain是域名还是ipv4地址,来分别生成 newHost全地址。
let newHost;
const ipv4Pattern = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
if (ipv4Pattern.test(targetDomain)) {
// IP地址直接为72.15.23.25 格式
newHost = targetDomain;
} else {
// 域名:使用带前缀stun.域名,替换域名。
newHost = hostname.replace(CONFIG.sourceDomain, targetDomain);
}
// 根据域名前缀动态选择协议(HTTP或HTTPS),对IPv4和IPv6都生效
protocol = getProtocolByHostname(hostname);
// 检查是否需要处理特殊URL格式
let pathAndSearch =
${url.pathname}${url.search};// 处理特殊URL格式
const processedPath = processSpecialUrlPath(url);
if (processedPath) {
// 如果返回了处理后的路径,使用它
pathAndSearch = processedPath;
}
const targetUrl =
${protocol}://${newHost}:${targetPort}${pathAndSearch};log(
重定向地址获取成功: ${targetUrl});// IPV6域名,或者非Proxy_PREFIXES列表前缀 直接返回302重定向
if ((isIPv6)||(!Proxy_PREFIXES.includes(prefix))) {
return Response.redirect(targetUrl, 302);
}
// ipv4 stun穿透 重新构造代理请求,进行转发:实现无端口地址访问(因为stun端口会动态变化)。
// 复制原始请求的头部,并修改Host头部
const newHeaders = new Headers(request.headers);
newHeaders.set('Host', newHost);
// newHeaders.delete('CF-Connecting-IP'); // 移除Cloudflare特定头
const modifiedRequest = new Request(targetUrl, {
method: request.method,
headers: newHeaders,
body: request.body,
redirect: 'manual',
});
return fetch(modifiedRequest);
/ fetch请求成功后直接返回转发内容,否则返回错误代码。
try {
const newResponse = await fetch(modifiedRequest);
// 可以在这里修改响应头
//const newResponse = new Response(response.body, response);
newResponse.headers.set('X-Proxy', 'Cloudflare-Worker');
return newResponse;
} catch (error) {
return new Response('Proxy Error', { status: 502 });
} /
}
function parseEnvVar(envObj, key, defaultValue = {}) {
try {
// 从环境变量对象中获取对应键的值
const valueString = envObj[key];
// 检查环境变量是否存在if (valueString === undefined || valueString === null) {
console.warn(`环境变量 ${key} 未设置,使用默认值。`);
return defaultValue;
}
console.warn(`环境变量 ${key} 解析成功:${valueString}。`);
// 将JSON字符串解析为JavaScript对象/数组/值
return JSON.parse(valueString);
} catch (e) {
// 处理解析错误(JSON格式无效、语法错误等)
console.error(
解析环境变量 ${key} 失败:, e.message);console.error(
原始值: ${envObj[key]?.substring(0, 100)}...); // 只打印前100字符return defaultValue;
}
}
/**
处理特殊URL路径
当URL包含 ?target=detail&path= 参数时,重新构建路径
@param {URL} url - 请求的URL对象
@returns {string|null} 处理后的路径,如果不需要处理则返回null
*/
function processSpecialUrlPath(url) {
const searchParams = url.searchParams;
// 检查是否包含特定的查询参数
if (searchParams.has('target') && searchParams.get('target') === 'detail' && searchParams.has('path')) {
// 获取原始路径参数
const originalPath = searchParams.get('path');
// 将+替换为空格
const decodedPath = originalPath.replace(/+/g, ' ');
// 返回处理后的路径(直接使用路径部分,不再包含查询参数)
return url.pathname + decodedPath;
}
return null;
}
/**
根据主机名获取对应的IPv6端口
@param {string} hostname - 请求的主机名
@returns {string} 对应的端口号
**/
function getIPv6PortByHostname(hostname) {
// 遍历端口映射配置,检查主机名是否包含对应的前缀
for (const [prefix, port] of Object.entries(IPV6_PORT_MAP)) {
if (hostname.includes(prefix + '.' + CONFIG.sourceDomain)) {
return port;
}
}
// 如果没有匹配到任何前缀,返回默认端口
return CONFIG.ipv6Port;
}
/**
根据主机名获取对应的IPv4地址或域名
@param {string} hostname - 请求的主机名
@returns {string} 对应的域名或IP地址
*/
function getIPv4Address(hostname) {
const ipv4address = [
'music1',
'XXXXXX'
// 可以继续添加其他需要直接重定向为ip地址的前缀,仅能在IPV4_PORT_MAP中选取
];
// 提取域名前缀(去掉源域名部分)
const sourceDomain = CONFIG.sourceDomain;
const prefixMatch = hostname.match(new RegExp(`^(.?)\.${sourceDomain.replace(/./g, '\.')}$`));
if (!prefixMatch || !prefixMatch[1]) {
// 如果没有匹配到前缀,返回域名
return CONFIG.ipv4TargetDomain;
}
const prefix = prefixMatch[1];
// 检查前缀是否在IPV4_PORT_MAP中
const isInIPv4Map = IPV4_PORT_MAP.hasOwnProperty(prefix);
// 检查前缀是否在HTTP_PREFIXES中
const isInHTTPPrefixes = ipv4address.includes(prefix);
// 域名前缀如果即在ipv4address中,又在IPV4_PORT_MAP中,则直接使用ipv4地址代替重定向后的域名,并且使用http访问。
// 匹配到的前缀,直接使用ip地址访问,可能会加快访问速度,如果存在问题,可以修改代码也返回域名CONFIG.ipv4TargetDomain
if (isInIPv4Map && isInHTTPPrefixes) {
log(
域名前缀 "${prefix}" 同时需要IPv4直连和HTTP协议,使用IP地址: ${CONFIG.ipv4Address});return CONFIG.ipv4Address;
}
// 其他情况返回域名
log(
域名前缀 "${prefix}" 使用域名反代: ${CONFIG.ipv4TargetDomain});return CONFIG.ipv4TargetDomain;
}
/**
根据主机名获取对应的IPv4端口
@param {string} hostname - 请求的主机名
@returns {string} 对应的端口号
**/
function getIPv4PortByHostname(hostname) {
// 遍历端口映射配置,检查主机名是否包含对应的前缀
for (const [prefix, port] of Object.entries(IPV4_PORT_MAP)) {
if (hostname.includes(prefix + '.' + CONFIG.sourceDomain)) {
if(port.includes('STUN')){
return CONFIG.ipv4Port;
}
return port;
}
}
// 如果没有匹配到任何前缀,返回默认端口
return CONFIG.ipv4Port;
}
/**
根据主机名获取对应的协议(对IPv4和IPv6都生效)
@param {string} hostname - 请求的主机名
@returns {string} 对应的协议(http或https)
*/
function getProtocolByHostname(hostname) {
// 检查主机名是否包含需要HTTP协议的前缀
for (const prefix of HTTP_PREFIXES) {
if (hostname.includes(prefix + '.' + CONFIG.sourceDomain)) {
return 'http'; // 使用HTTP协议
}
}
// 如果没有匹配到任何需要HTTP的前缀,返回HTTPS协议
return 'https';
}
// 自定义日志函数(使用北京时间)
function log(message, level = 'INFO') {
const now = new Date();
// UTC时间 + 8小时 = 北京时间
const beijingTime = new Date(now.getTime() + 8 60 60 1000);
const timestamp = beijingTime.toISOString()
.replace('T', ' ')
.replace(/.d+Z$/, '');
console.log(
${timestamp} - ${level} - ${message});}
基本都是参照博客主的不同文章杂揉的