在面试中,面试官通常希望看到你不仅会写代码,更理解代码背后的计算机网络原理和系统架构思维。我们将围绕“跨域问题”这一核心痛点,从浏览器同源策略讲到 Node.js 服务端,再到 Nginx 反向代理,构建一个完美的回答闭环。
第一部分:浏览器与网络基础 —— “同源策略与跨域的本质”
面试官提问: “在开发中遇到过跨域问题吗?你是怎么解决的?原理是什么?”
1. 核心概念:什么是同源策略 (Same-Origin Policy)?
同源策略是浏览器的一种安全机制,由 Netscape 在 1995 年提出。它的核心目的是为了隔离潜在恶意文档,防止一个来源的脚本去访问或操作另一个来源的敏感数据。
- 判定标准:协议 (Protocol)、域名 (Domain)、端口 (Port) 三者必须完全一致。只要有一个不同,浏览器就会拦截请求,这就是“跨域”。
2. 常见解决方案对比
在现代开发中,我们通常有以下几种手段来突破同源策略:
| 方案 | 适用场景 | 原理与优缺点 |
|---|---|---|
| CORS (跨域资源共享) | 现代 Web 开发首选 | 后端设置 HTTP 头 (如 Access-Control-Allow-Origin)。优点:支持所有 HTTP 方法,安全;缺点:需要后端配合。 |
| Nginx 反向代理 | 线上生产环境 | 利用 Nginx 转发请求,让前后端看起来是同源的。优点:彻底解决跨域,性能高;缺点:需要运维配置。 |
| Vite/Webpack Proxy | 本地开发环境 | 开发服务器的代理功能。优点:前端独立配置,无需后端介入;缺点:仅限开发环境。 |
| JSONP | 老旧系统兼容 | 利用 <script> 标签不受同源策略限制的特性。优点:兼容性极好;缺点:仅支持 GET,易受 XSS 攻击,已被 CORS 取代。 |
| WebSocket | 双向通信场景 | 基于 TCP,不涉及 HTTP 同源策略。 |
3. 深入一点:CORS 的预检请求 (Preflight Request)
这是一个高频考点。当请求满足以下任一条件时,浏览器会先发送一个 OPTIONS 请求进行“预检”:
- 请求方法不是 GET、POST 或 HEAD(例如 PUT、DELETE)。
- 请求头包含自定义字段(如
X-Custom-Header)。 Content-Type不是application/x-www-form-urlencoded、multipart/form-data或text/plain(例如application/json)。
面试官心理:如果你能讲出 OPTIONS 预检和 Access-Control-Max-Age(缓存预检结果的时间),面试官会觉得你有扎实的网络基础。
第二部分:Node.js 服务端 —— “手写一个 CORS 中间件”
面试官提问: “如果让你自己写一个 Node.js 服务来处理跨域,你会怎么写?”
我们可以结合你提供的代码,展示你对原生 Node.js 的掌握能力。
1. 核心逻辑解析
在原生 Node.js 中,处理跨域的关键在于设置响应头以及正确处理 OPTIONS 请求。
1const http = require('http');
2
3const server = http.createServer((req, res) => {
4 // 1. 定义 CORS 响应头
5 const headers = {
6 'Access-Control-Allow-Origin': 'http://localhost', // 指定允许的源(生产环境建议动态校验)
7 'Access-Control-Allow-Methods': 'GET, PUT, POST, DELETE', // 允许的方法
8 'Access-Control-Allow-Headers': 'Content-Type, X-Custom-Header', // 允许的自定义头部
9 'Access-Control-Max-Age': '86400' // 预检请求缓存 24 小时
10 };
11
12 // 2. 处理预检请求 (OPTIONS)
13 if (req.method === 'OPTIONS') {
14 // 浏览器在发 PUT/POST 等复杂请求前,会先发 OPTIONS 探路
15 res.writeHead(204, headers); // 204 No Content
16 res.end();
17 return;
18 }
19
20 // 3. 处理业务请求 (例如 PUT)
21 if (req.method === 'PUT' && req.url === '/data') {
22 let body = '';
23 req.on('data', chunk => body += chunk.toString());
24
25 req.on('end', () => {
26 console.log('Received data:', body);
27 res.writeHead(200, headers); // 业务逻辑处理完后,依然要带上 CORS 头
28 res.end(JSON.stringify({status: 'success'}));
29 });
30 } else {
31 res.writeHead(404, headers);
32 res.end('Not Found');
33 }
34});
💡 面试话术:
“我这里展示的是原生 Node.js 的处理逻辑。在实际项目中,我会使用
cors中间件,但理解原生实现有助于排查问题。核心在于两点:一是给所有响应加上Access-Control-Allow-Origin;二是拦截OPTIONS请求并返回 204 状态码,告诉浏览器‘你可以继续发真实请求了’。”
第三部分:构建工具与工程化 —— “Vite 的开发期代理”
面试官提问: “你们本地开发是怎么联调后端接口的?”
1. Vite Proxy 的配置
在开发环境下,前端通常跑在 http://localhost:5173,而后端在 http://localhost:3000。利用 Vite 的开发服务器代理功能,我们可以让前端代码“以为”自己在请求同源接口。
1export default defineConfig({
2 server: {
3 proxy: {
4 '/api': {
5 target: 'http://localhost:3000', // 实际后端地址
6 changeOrigin: true, // 关键:改变请求头中的 Origin,让后端以为请求是发给它自己的
7 rewrite: (path) => path.replace(/^/api/, '') // 重写路径,去掉代理前缀
8 }
9 }
10 }
11})
💡 面试话术:
“我们使用 Vite 的
server.proxy配置。它的原理是 Vite 开发服务器作为一个‘中间人’。当浏览器请求/api/user时,Vite 拦截该请求,将其转发给后端 3000 端口。因为这是服务器端(Node.js)之间的通信,不受浏览器同源策略限制。Vite 再把结果返回给浏览器。这种方式配置简单,且只在开发环境生效,非常适合全栈开发流程。”
第四部分:Nginx 架构 —— “线上环境的终极解决方案”
面试官提问: “线上环境没有 Vite 了,你们是怎么处理跨域和性能的?”
1. Nginx 反向代理配置解析
1server {
2 listen 80;
3 server_name www.baidu.com; # 线上域名
4
5 # 1. 静态资源服务
6 location / {
7 root /usr/share/nginx/html; # 指向 Vue/React 打包后的 index.html
8 index index.html;
9 # try_files 指令常用于 SPA 路由回退,防止刷新 404
10 }
11
12 # 2. 核心:API 接口代理
13 location /api/ {
14 # 将请求转发给后端集群或具体服务
15 proxy_pass http://localhost:3000/;
16
17 # 3. 关键 Header 设置
18 # 让后端获取到真实的客户端 IP,而不是 Nginx 的 IP
19 proxy_set_header Host $host;
20 proxy_set_header X-Real-IP $remote_addr;
21 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
22
23 # 如果后端需要区分 HTTPS,传递原始协议
24 proxy_set_header X-Forwarded-Proto $scheme;
25
26 # 4. 性能与调试
27 proxy_cache_bypass $http_upgrade; # 针对 WebSocket 或升级请求关闭缓存
28 }
29}
2. 为什么线上必须用 Nginx?
结合搜索到的百度首页(www.baidu.com)来看,我们可以这样回答:
- 性能优化:Nginx 是 C 语言编写的高性能服务器,处理静态文件(HTML/CSS/JS)的效率远高于 Node.js。百度首页每天有千亿级请求,必须由 Nginx 直接返回静态资源,减轻后端压力。
- 安全隔离:Nginx 作为“门面”,可以隐藏后端服务器的真实 IP 和端口,防止直接暴露给公网。
- 负载均衡:在百度这种大流量场景下,Nginx 可以将请求分发到后端不同的 Node.js 或 Java 服务器集群上,保证高可用。
- 统一入口:Nginx 将前端页面(
/)和后端接口(/api)整合在同一个域名下,彻底解决了浏览器跨域问题。
💡 面试话术:
“在生产环境,我们使用 Nginx 反向代理。比如百度首页,Nginx 80 端口接收所有流量。对于静态资源(HTML/CSS/JS),Nginx 直接返回,速度极快;对于
/api开头的请求,Nginx 通过proxy_pass转发给后端 Node.js 服务。这样,前端页面和接口都在同一个域名下,从根本上消除了跨域问题,同时也利用了 Nginx 的高性能和负载均衡能力。”
第五部分:总结 —— 面试中的“黄金法则”
在面试的最后,你可以总结一下你的技术选型思维,这会让面试官觉得你是一个有架构思维的开发者:
- 开发环境 (Dev) :使用 Vite/Webpack Proxy。理由是配置在前端工程内,不依赖外部环境,启动即用,适合本地调试。
- 测试/线上环境 (Prod) :使用 Nginx 反向代理。理由是安全、高性能、支持负载均衡,且是运维标准方案。
- 特殊情况:如果是微服务架构或需要第三方系统调用,才考虑后端配置 CORS。
通过这样的回答,你展示了从浏览器底层原理到Node.js 服务端逻辑,再到Nginx 运维架构的全链路知识,这正是高级开发工程师的核心竞争力。