server.js 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  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. * 反代终止 TLS 后须传 `X-Forwarded-Proto: https`;若漏传,协议会判成 http。
  11. * 若 Host 误带内网端口,与 NEXT_PUBLIC_SITE_URL 对齐时去掉端口。
  12. */
  13. function normalizeProxyHeaders(req) {
  14. const site = process.env.NEXT_PUBLIC_SITE_URL?.trim().replace(/\/$/, '');
  15. if (!site || process.env.NODE_ENV !== 'production') return;
  16. let canonicalHost;
  17. try {
  18. canonicalHost = new URL(site).hostname;
  19. } catch {
  20. return;
  21. }
  22. const allowed = new Set([canonicalHost, `www.${canonicalHost}`]);
  23. const raw = req.headers.host;
  24. if (typeof raw !== 'string') return;
  25. const colon = raw.indexOf(':');
  26. const hostnameOnly = colon === -1 ? raw : raw.slice(0, colon);
  27. if (!allowed.has(hostnameOnly)) return;
  28. if (colon !== -1) {
  29. req.headers.host = hostnameOnly;
  30. }
  31. if (site.startsWith('https:') && !req.headers['x-forwarded-proto']) {
  32. req.headers['x-forwarded-proto'] = 'https';
  33. }
  34. }
  35. // port 必须与 listen 一致,避免 Next 内部仍按 3000 拼绝对 URL(勿改 hostname,以免内部 initURL 变成 127.0.0.1)
  36. const app = next({ dev: false, port });
  37. const handle = app.getRequestHandler();
  38. app.prepare().then(() => {
  39. const server = createServer((req, res) => {
  40. normalizeProxyHeaders(req);
  41. handle(req, res, parse(req.url, true));
  42. });
  43. server.on('error', (error) => {
  44. if (error.code === 'EADDRINUSE') {
  45. const fallbackPort = port + 1;
  46. console.warn(`Port ${port} is in use, retrying on ${fallbackPort}...`);
  47. server.listen(fallbackPort);
  48. return;
  49. }
  50. throw error;
  51. });
  52. server.listen(port);
  53. });