Problem Tracking
Accessing the website displays Access denied or API service issue

Website errorpm2 logs shiro --lines 50 and find error ETIMEDOUT

Log errorcurl command can get a 200 response
fetch throws AggregateError [ETIMEDOUT], stack stops at internalConnectMultiple
Problem Analysis
Node 20+ has a defect in the implementation of “Happy Eyeballs” causing a typical phenomenon: Node, in order to “automatically select the route” between IPv4/IPv6, first tries the first record, giving only 250 ms ⏱️; if the handshake does not succeed, it forcefully cancels and then tries the other protocol family, resulting in possible timeouts on both sides. Curl does not use this mechanism by default, so it always succeeds.
Fix Plan
Check Node Version
node -v # 若 ≥ 20 则命中此 Bug
One-time Verification
NODE_OPTIONS="--network-family-autoselection-attempt-timeout=5000" node -e "await fetch('https://server.gufei.life/api/v2/aggregate?theme=shiro').then(r=>console.log(r.status))"
# 如果打印 200,说明问题消除
Output 200, indicating that Happy Eyeballs' default 250 ms handshake window is the culprit; after enlarging the window to 5 s, everything works fine.
Modify ecosystem.config.js file
/**
* /root/shiro/ecosystem.config.js
*/
module.exports = {
apps: [
{
name: 'shiro',
script: 'server.js',
autorestart: true,
watch: false,
max_memory_restart: '500M',
/* 方式 A——通过环境变量注入给 Node */
env: {
PORT: 2323,
NODE_ENV: 'production',
NEXT_SHARP_PATH: process.env.NEXT_SHARP_PATH,
NODE_OPTIONS:
'--network-family-autoselection-attempt-timeout=5000 --dns-result-order=ipv4first',
},
/* 方式 B——直接让 PM2 把参数作为 node_args 传递 */
node_args:
'--network-family-autoselection-attempt-timeout=5000 --dns-result-order=ipv4first',
log_date_format: 'YYYY-MM-DD HH:mm:ss',
},
],
};
Restart the service to apply the configuration
pm2 delete shiro # 清掉旧进程(日志文件仍在)
cd /root/shiro
pm2 start /root/shiro/ecosystem.config.js
Check if the fix is complete
pm2 logs shiro --lines 50
As long as the logs no longer have
AggregateError [ETIMEDOUT] / TypeError: fetch failed
it means the fix has fully taken effect.