Issue Tracking
Accessing the website shows Access forbidden or API service has a problem

Website errorpm2 logs shiro --lines 50 reveals the error ETIMEDOUT

Log error
Using the curl command returns a 200 response
fetch throws AggregateError [ETIMEDOUT], with the stack trace stopping at internalConnectMultiple
Issue Analysis
This is a typical symptom caused by Node 20+ "Happy Eyeballs" implementation defect:
To "auto-select routes" between IPv4/IPv6, Node first tries the first record and only gives it 250 ms ⏱️; if the handshake doesn't succeed, it forcibly cancels and tries the other protocol family, resulting in both sides potentially timing out.
Curl doesn't use this mechanism by default, so it always succeeds.
Fix
Check Node Version
node -v # If ≥ 20, this bug is triggered
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))"
# If it prints 200, the issue is resolved
Output 200 confirms that Happy Eyeballs' default 250 ms handshake window was the root cause; extending the window to 5 s makes everything work normally.
Modify the 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',
/* Method A — Inject via environment variables to 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',
},
/* Method B — Have PM2 pass the arguments directly as 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
Check if the Fix is Complete
As long as the logs no longer show
AggregateError [ETIMEDOUT] / TypeError: fetch failed
it means the fix has fully taken effect.