server.js 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. /* eslint-disable @typescript-eslint/no-require-imports */
  2. const { createServer } = require('http');
  3. const { parse } = require('url');
  4. const next = require('next');
  5. const port = Number(process.env.PORT || 4000);
  6. /**
  7. * 自定义 server 若写 `next({ dev: false })` 不传 port,Next 内部仍默认 3000(见 next/dist/server/next.js),
  8. * 与 `listen(4000)` 不一致时,307 Location 会变成 `http://jinclab.com:3000/zh` 这类错误地址。
  9. *
  10. * 默认 4000 与常见 Nginx `proxy_pass http://127.0.0.1:4000` 一致;勿随意改默认,否则反代未同步会 502。
  11. *
  12. * 反代终止 TLS 后须传 `X-Forwarded-Proto: https`;若漏传,协议会判成 http。
  13. * 若 Host 误带内网端口,与 NEXT_PUBLIC_SITE_URL 对齐时去掉端口。
  14. */
  15. function normalizeProxyHeaders(req) {
  16. const site = process.env.NEXT_PUBLIC_SITE_URL?.trim().replace(/\/$/, '');
  17. if (!site || process.env.NODE_ENV !== 'production') return;
  18. let canonicalHost;
  19. try {
  20. canonicalHost = new URL(site).hostname;
  21. } catch {
  22. return;
  23. }
  24. const allowed = new Set([canonicalHost, `www.${canonicalHost}`]);
  25. const raw = req.headers.host;
  26. if (typeof raw !== 'string') return;
  27. const colon = raw.indexOf(':');
  28. const hostnameOnly = colon === -1 ? raw : raw.slice(0, colon);
  29. if (!allowed.has(hostnameOnly)) return;
  30. if (colon !== -1) {
  31. req.headers.host = hostnameOnly;
  32. }
  33. if (site.startsWith('https:') && !req.headers['x-forwarded-proto']) {
  34. req.headers['x-forwarded-proto'] = 'https';
  35. }
  36. }
  37. // port 必须与 listen 一致,避免 Next 内部仍按 3000 拼绝对 URL(勿改 hostname,以免内部 initURL 变成 127.0.0.1)
  38. const app = next({ dev: false, port });
  39. const handle = app.getRequestHandler();
  40. app.prepare().then(() => {
  41. const server = createServer((req, res) => {
  42. normalizeProxyHeaders(req);
  43. handle(req, res, parse(req.url, true));
  44. });
  45. server.on('error', (error) => {
  46. if (error.code === 'EADDRINUSE') {
  47. const fallbackPort = port + 1;
  48. console.warn(`Port ${port} is in use, retrying on ${fallbackPort}...`);
  49. server.listen(fallbackPort);
  50. return;
  51. }
  52. throw error;
  53. });
  54. server.listen(port);
  55. });