|
|
@@ -2,24 +2,57 @@
|
|
|
const { createServer } = require('http');
|
|
|
const { parse } = require('url');
|
|
|
const next = require('next');
|
|
|
-
|
|
|
+
|
|
|
const port = Number(process.env.PORT || 4000);
|
|
|
-const app = next({ dev: false });
|
|
|
+
|
|
|
+/**
|
|
|
+ * 自定义 server 若写 `next({ dev: false })` 不传 port,Next 内部仍默认 3000(见 next/dist/server/next.js),
|
|
|
+ * 与 `listen(4000)` 不一致时,307 Location 会变成 `http://jinclab.com:3000/zh` 这类错误地址。
|
|
|
+ *
|
|
|
+ * 反代终止 TLS 后须传 `X-Forwarded-Proto: https`;若漏传,协议会判成 http。
|
|
|
+ * 若 Host 误带内网端口,与 NEXT_PUBLIC_SITE_URL 对齐时去掉端口。
|
|
|
+ */
|
|
|
+function normalizeProxyHeaders(req) {
|
|
|
+ const site = process.env.NEXT_PUBLIC_SITE_URL?.trim().replace(/\/$/, '');
|
|
|
+ if (!site || process.env.NODE_ENV !== 'production') return;
|
|
|
+
|
|
|
+ let canonicalHost;
|
|
|
+ try {
|
|
|
+ canonicalHost = new URL(site).hostname;
|
|
|
+ } catch {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const allowed = new Set([canonicalHost, `www.${canonicalHost}`]);
|
|
|
+ const raw = req.headers.host;
|
|
|
+ if (typeof raw !== 'string') return;
|
|
|
+
|
|
|
+ const colon = raw.indexOf(':');
|
|
|
+ const hostnameOnly = colon === -1 ? raw : raw.slice(0, colon);
|
|
|
+
|
|
|
+ if (!allowed.has(hostnameOnly)) return;
|
|
|
+
|
|
|
+ if (colon !== -1) {
|
|
|
+ req.headers.host = hostnameOnly;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (site.startsWith('https:') && !req.headers['x-forwarded-proto']) {
|
|
|
+ req.headers['x-forwarded-proto'] = 'https';
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// port 必须与 listen 一致,避免 Next 内部仍按 3000 拼绝对 URL(勿改 hostname,以免内部 initURL 变成 127.0.0.1)
|
|
|
+const app = next({ dev: false, port });
|
|
|
const handle = app.getRequestHandler();
|
|
|
-
|
|
|
-app.prepare().then(() =>
|
|
|
-{
|
|
|
- const server = createServer((req, res) =>
|
|
|
- {
|
|
|
- // Be sure to pass `true` as the second argument to `url.parse`.
|
|
|
- // This tells it to parse the query portion of the URL.
|
|
|
+
|
|
|
+app.prepare().then(() => {
|
|
|
+ const server = createServer((req, res) => {
|
|
|
+ normalizeProxyHeaders(req);
|
|
|
handle(req, res, parse(req.url, true));
|
|
|
});
|
|
|
|
|
|
- server.on('error', (error) =>
|
|
|
- {
|
|
|
- if (error.code === 'EADDRINUSE')
|
|
|
- {
|
|
|
+ server.on('error', (error) => {
|
|
|
+ if (error.code === 'EADDRINUSE') {
|
|
|
const fallbackPort = port + 1;
|
|
|
console.warn(`Port ${port} is in use, retrying on ${fallbackPort}...`);
|
|
|
server.listen(fallbackPort);
|
|
|
@@ -29,4 +62,4 @@ app.prepare().then(() =>
|
|
|
});
|
|
|
|
|
|
server.listen(port);
|
|
|
-});
|
|
|
+});
|