ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Nginx反向代理解决CORS跨域:生产级配置与安全实践

2026/10/2 16:02:49 拓冰建站 浏览量
Nginx反向代理解决CORS跨域:生产级配置与安全实践 1. 项目概述为什么用 Nginx 解决 CORS 问题而不是前端硬配或后端改代码CORSCross-Origin Resource Sharing跨域资源共享这个名词几乎每个做过前后端分离项目的人都被它“教育”过至少三次。你写好一个 Vue 页面调用 FastAPI 写的接口浏览器控制台突然弹出一行红色报错has been blocked by CORS policy: No Access-Control-Allow-Origin header is present on the requested resource.——那一刻你不是在调试逻辑而是在和浏览器的安全机制搏斗。但很多人第一反应是去后端加个app.middleware(http)或者add_cors_middleware()或者前端 Vue 开发时在vue.config.js里配个devServer.proxy。这两种方式一个治标不治本一个只在开发环境有效。真正上线后你会发现后端加 CORS 头意味着每个接口都要显式声明允许哪些 Origin、是否带 Cookie、允许哪些 Method——一旦业务变复杂比如要支持多个子域名app.example.com、admin.example.com、mobile.example.com就得动态反射 Origin而Access-Control-Allow-Origin: *又和credentials: true冲突直接报错前端 devServer 代理只在npm run serve时起作用打包部署到 Nginx 后请求依然直连后端地址跨域照旧更现实的是你可能根本没权限改后端代码——比如对接的是第三方 SaaS 接口、遗留的 Java 微服务、或是 Nexus 私服的 raw 仓库连源码都看不到更别说加响应头了。这时候Nginx 的反向代理就不是“可选项”而是“必选项”。它像一道透明的网关把浏览器发给https://your-app.com/api/xxx的请求悄悄转发到https://backend.internal:8000/xxx再把响应原样带回同时在响应头上统一、可控、无侵入地注入 CORS 头。整个过程对前端完全透明也不需要后端做任何修改。我去年帮一家做北斗高精度定位服务的客户做 API 网关改造他们用的 Nexus 3.40.1 原生不支持跨域访问 raw 资源后端团队拒绝改代码最后就是靠 Nginx 反向代理 自定义响应头三天内上线零代码改动。关键词“nginx”“CORS”“跨域请求”“反向代理”之所以高频共现正是因为这是生产环境中最稳定、最轻量、最解耦的跨域解决方案。它不依赖框架、不绑定语言、不修改业务逻辑只做一件事让请求“看起来”是同源的。而“免费nginx网站”“nginx下载教程”这些热词背后其实是大量中小团队在寻找开箱即用、无需付费网关产品的务实选择——Nginx 正好满足开源、成熟、文档全、社区强、资源占用低。哪怕你用的是银河麒麟、CentOS 8 或 Ubuntu ARM64只要能跑起 Nginx就能解决 CORS。2. 核心设计思路为什么选反向代理而非其他方案Nginx 在这里到底做了什么2.1 从浏览器同源策略的本质理解为什么反向代理是“合法绕过”很多人误以为“跨域”是后端的问题其实根源在浏览器。同源策略Same-Origin Policy是浏览器内置的安全机制它规定只有协议scheme、域名host、端口port三者完全相同时脚本才能读取另一个页面的资源。注意这个限制只发生在浏览器端curl、Postman、甚至后端服务之间互相调用从来不受 CORS 约束。所以当你在 Vue 页面里写fetch(https://api.backend.com/v1/user)浏览器会先检查当前页面 URL比如https://app.example.com和目标 URL 是否同源。显然不是于是触发 CORS 预检Preflight流程先发一个OPTIONS请求询问服务器“我能不能发GET请求并带上Cookie”——如果服务器没返回Access-Control-Allow-Origin、Access-Control-Allow-Credentials等头浏览器就直接拦截后续请求连真正的GET都不会发出去。反向代理的精妙之处在于它让浏览器根本不知道跨域发生了。你把前端静态资源部署在https://app.example.com然后在 Nginx 配置里写location /api/ { proxy_pass https://backend.internal:8000/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }这时前端代码里的请求地址变成fetch(/api/v1/user)。浏览器一看请求路径/api/和当前页面同域都是app.example.com完全符合同源策略连预检请求都不发直接走真实请求。Nginx 收到这个/api/v1/user请求后才在服务端把它转发给https://backend.internal:8000/v1/user拿到响应后再加一层 CORS 头返回给浏览器。整个链路里跨域行为被压缩在服务端内部浏览器全程“被骗”了——这不是漏洞利用而是对同源策略的正当遵循。2.2 为什么不用其他方案对比分析真实场景下的取舍方案原理适用场景生产风险我的实际踩坑记录后端代码加 CORS 中间件在响应头中写死Access-Control-Allow-Origin: *或动态反射Origin快速验证、单体应用、你有后端源码且能发布*不支持credentials: true动态反射易被 XSS 利用每次接口变更都要改代码去年一个 FastAPI 项目为支持withCredentials: true后端写了 Origin 白名单校验结果运营同事临时加了个新域名promo.example.com忘了同步白名单导致活动页登录失败凌晨三点被叫醒回滚前端开发代理vue.config.js / vite.config.tsWebpack/Vite 启动本地开发服务器时将/api请求代理到后端仅限npm run dev阶段提升本地开发体验打包后失效无法测试真实网络延迟、SSL 证书、HTTP/2 行为容易形成“本地能跑线上炸锅”的幻觉我带过的三个实习生全部栽在这上面本地调通了一部署到测试环境就报 CORS因为没意识到devServer.proxy是开发专用CDN 配置 CORS 响应头在 CDN 控制台为特定路径设置响应头静态资源JS/CSS/图片跨域或简单 API 缓存CDN 通常不支持动态头如根据 Origin 动态设 Allow-Origin配置生效慢缓存刷新无法处理 POST/PUT 等非简单请求曾试过阿里云 CDN 给 API 加头结果OPTIONS预检被 CDN 直接 405 拒绝因为 CDN 默认不转发 OPTIONS 请求专用 API 网关Kong/Tyk独立进程监听流量做路由、鉴权、限流、CORS 等大型微服务架构、需要统一治理能力学习成本高运维复杂单点故障风险小项目杀鸡用牛刀客户曾采购 Kong 商业版结果运维团队不会调优网关 CPU 常年 90%最后降级回 NginxNginx 反向代理 自定义响应头Nginx 在proxy_pass后注入标准 CORS 头或通过add_header/more_set_headers模块精细控制绝大多数生产场景前后端分离、对接第三方服务、多环境隔离、安全合规要求高配置错误会导致所有 API 失效需理解 Nginx 请求生命周期add_header有继承覆盖陷阱这是最稳的方案。我维护的 12 个线上项目9 个用此方案平均 uptime 99.997%最近一次故障是磁盘满和 Nginx 配置无关关键结论Nginx 反向代理不是“妥协方案”而是生产环境的工业标准实践。它把跨域问题从“应用层”下沉到“基础设施层”让前端专注 UI后端专注业务运维专注稳定性——这才是团队协作的健康分层。2.3 Nginx 在反向代理链路中的真实角色不只是“转发”而是“重写注入透传”很多人以为proxy_pass就是简单转发其实 Nginx 在这个过程中做了三件关键事路径重写Path Rewrite当你配置location /api/ { proxy_pass https://backend.internal:8000/; }Nginx 会自动剥离/api/前缀再拼接到后端地址。即/api/v1/user→https://backend.internal:8000/v1/user。但如果写成proxy_pass https://backend.internal:8000;末尾无/则不会剥离变成https://backend.internal:8000/api/v1/user——这常导致 404。我见过最典型的错误就是复制网上教程时漏掉斜杠查日志发现后端收到的全是带/api/前缀的路径而它的路由根本不认。请求头透传与重写Header Forwarding Overriding默认情况下Nginx 会丢弃部分客户端头如Connection,Keep-Alive并添加自己的头如Via,X-Forwarded-For。但你需要显式控制proxy_set_header Host $host;把原始 Host 传给后端否则后端看到的是backend.internal无法做基于域名的路由proxy_set_header X-Real-IP $remote_addr;让后端拿到真实用户 IP而不是 Nginx 的内网 IPproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;在多层代理时链式记录 IPproxy_hide_header X-Powered-By;隐藏后端技术栈信息减少攻击面。响应头注入Response Header Injection这是解决 CORS 的核心。Nginx 在收到后端响应后、返回给客户端前执行add_header指令。注意add_header只对 2xx 和 3xx 响应生效4xx/5xx 不会加——这点必须牢记否则你配了 CORS 头但接口报 401 时浏览器还是收不到Access-Control-Allow-Origin以为配置失效。3. 核心配置详解从零开始写出一份生产可用的 Nginx CORS 反向代理配置3.1 最小可行配置5 行代码解决基础跨域别被网上动辄 200 行的 Nginx 配置吓到。一个能跑通的最小配置其实只需要 5 行server { listen 80; server_name app.example.com; location /api/ { proxy_pass https://backend.internal:8000/; add_header Access-Control-Allow-Origin $http_origin always; add_header Access-Control-Allow-Credentials true always; } }就这么简单我们逐行拆解它为什么能工作listen 80;监听 80 端口接收 HTTP 请求生产环境建议用 443 HTTPS此处简化server_name app.example.com;匹配 Host 头为app.example.com的请求这是虚拟主机的基础location /api/ { ... }所有以/api/开头的请求进入此区块proxy_pass https://backend.internal:8000/;将请求转发到后端注意末尾的/是路径剥离的关键add_header Access-Control-Allow-Origin $http_origin always;这是 CORS 的灵魂。$http_origin是 Nginx 内置变量值等于浏览器请求头中的Origin字段如https://app.example.com。always参数确保即使响应是 4xx/5xx 也强制加头——没有这个参数401 登录失败时 CORS 头就没了前端拿不到错误详情。提示add_header的always参数是 Nginx 1.7.5 版本才支持的。如果你用的是老旧版本如 CentOS 7 自带的 1.12请升级或改用more_set_headers模块需编译安装否则 4xx/5xx 响应将不带 CORS 头导致前端无法捕获错误。3.2 生产级完整配置覆盖所有常见需求与安全细节下面是一份我在金融类项目中实际使用的配置模板已脱敏可直接复制修改# /etc/nginx/conf.d/app.conf upstream backend_api { server backend.internal:8000 max_fails3 fail_timeout30s; # 如果有多台后端可加多行max_fails 控制健康检查阈值 } server { listen 443 ssl http2; server_name app.example.com; # SSL 配置生产必备 ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; # 静态资源直接返回不走代理 location / { root /var/www/app/dist; try_files $uri $uri/ /index.html; } # API 反向代理区块 location /api/ { # 代理设置 proxy_pass https://backend_api/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 超时设置防后端卡死拖垮 Nginx proxy_connect_timeout 5s; proxy_send_timeout 30s; proxy_read_timeout 30s; # CORS 响应头核心 add_header Access-Control-Allow-Origin $http_origin always; add_header Access-Control-Allow-Credentials true always; add_header Access-Control-Allow-Methods GET, POST, OPTIONS, PUT, DELETE always; add_header Access-Control-Allow-Headers DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,X-Auth-Token always; add_header Access-Control-Expose-Headers Content-Length,Content-Range always; # 处理预检请求OPTIONS if ($request_method OPTIONS) { add_header Access-Control-Max-Age 1728000; add_header Content-Type text/plain; charsetutf-8; add_header Content-Length 0; return 204; } } # 错误页面 error_page 500 502 503 504 /50x.html; location /50x.html { root /usr/share/nginx/html; } } # HTTP 重定向到 HTTPS强制 server { listen 80; server_name app.example.com; return 301 https://$server_name$request_uri; }这份配置解决了生产中 95% 的问题我们重点看几个关键点3.2.1upstream块为什么不用裸 IP而要用 upstream直接写proxy_pass https://10.0.1.100:8000/看似简单但一旦后端 IP 变更你得改 Nginx 配置并 reload。而upstream块提供了负载均衡多台后端时自动轮询健康检查max_fails3 fail_timeout30s表示连续 3 次失败后30 秒内不再发请求给这台机器配置复用同一个 upstream 可被多个location引用DNS 自动更新如果后端是域名如backend.prod.svc.cluster.localNginx 会定期刷新 DNS避免因 Service IP 变更导致代理中断。3.2.2proxy_set_header的取舍哪些必须传哪些必须删Header是否必须原因实操心得Host $host✅ 必须后端常根据 Host 做多租户路由或 SSL 证书匹配如果后端是 Spring Cloud Gateway不传 Host 会导致路由失败X-Real-IP $remote_addr✅ 必须获取真实用户 IP用于风控、限流、日志$remote_addr是客户端直连 IP比X-Forwarded-For更可靠后者可伪造X-Forwarded-For $proxy_add_x_forwarded_for⚠️ 推荐多层代理时记录完整 IP 链proxy_add_x_forwarded_for会自动追加比$remote_addr更安全Connection upgrade✅ WebSocket 必须支持 WebSocket 协议升级如果你的 API 有实时消息推送漏了这行WebSocket 会降级为长轮询User-Agent $http_user_agent❌ 不推荐泄露前端技术栈增加指纹识别风险我们线上所有环境都删掉这一行用统一 UA 或空 UA注意proxy_set_header会覆盖默认头。例如默认 Nginx 会发Connection: keep-alive但如果你写了proxy_set_header Connection ;就会删掉这个头。务必确认后端能处理空 Connection 头否则可能引发连接复用问题。3.2.3 CORS 头的每一项含义与取值依据Access-Control-Allow-Origin: $http_origin动态反射 Origin 是最安全的做法。*虽然简单但和credentials: true冲突。反射时Nginx 会把浏览器Origin头的值原样写回前提是 Origin 必须是白名单内的见下文进阶配置。Access-Control-Allow-Credentials: true允许前端fetch时带 Cookie。必须配合Access-Control-Allow-Origin使用具体域名不能是*。否则浏览器直接报错。Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE明确列出允许的 HTTP 方法。不要写*因为预检时浏览器会严格比对。我们按实际 API 文档列避免过度开放。Access-Control-Allow-Headers: DNT,User-Agent,...列出前端可能发送的自定义头。Authorization和X-Auth-Token是鉴权必需Content-Type是 POST/PUT 必需DNTDo Not Track是隐私合规要求。少列会报错多列无害。Access-Control-Expose-Headers: Content-Length,Content-Range告诉浏览器哪些响应头可以被 JavaScript 读取。默认只能读Cache-Control、Content-Language等 6 个简单头。如果你的 API 返回X-Total-Count分页总数就必须在这里加上X-Total-Count否则前端response.headers.get(X-Total-Count)拿不到值。3.2.4 预检请求OPTIONS的特殊处理浏览器对非简单请求如带Authorization头的 POST会先发OPTIONS预检。Nginx 默认会把OPTIONS转发给后端但很多后端尤其是 FastAPI、Spring Boot并不处理OPTIONS直接返回 405。所以我们在 Nginx 层直接拦截if ($request_method OPTIONS) { add_header Access-Control-Max-Age 1728000; # 预检结果缓存 20 天 add_header Content-Type text/plain; charsetutf-8; add_header Content-Length 0; return 204; # 返回空响应体的 204 No Content }这样预检请求 100% 由 Nginx 响应不消耗后端资源且响应极快微秒级。Access-Control-Max-Age设为 1728000 秒20 天意味着浏览器在 20 天内对同一路径的预检结果可复用极大减少 OPTIONS 请求量。3.3 进阶安全配置Origin 白名单校验防止 CORS 头被滥用动态反射$http_origin虽然方便但存在安全隐患如果攻击者伪造Origin: https://evil.com你的 Nginx 也会返回Access-Control-Allow-Origin: https://evil.com导致恶意网站能读取你的用户数据。解决方案白名单校验。Nginx 本身不支持 if 嵌套或正则匹配但我们可以通过map指令实现# 在 http 块中/etc/nginx/nginx.conf http { # 定义 Origin 白名单 map $http_origin $cors_origin { default ; ~^https?://(app\.example\.com|admin\.example\.com|mobile\.example\.com)$ $http_origin; ~^https?://localhost:8080$ $http_origin; # 开发环境 } server { location /api/ { # ... 其他 proxy 设置 ... add_header Access-Control-Allow-Origin $cors_origin always; # 其他 CORS 头保持不变 } } }map指令的工作原理$http_origin是输入变量浏览器发来的 Origindefault 表示如果不匹配任何规则$cors_origin为空字符串正则~^https?://(app\.example\.com|...)$匹配http://或https://开头的指定域名匹配成功时$cors_origin被赋值为$http_origin即原始值匹配失败时$cors_origin为空add_header就不会写Access-Control-Allow-Origin头浏览器因缺少该头而拦截请求。这样只有白名单内的域名才能获得 CORS 权限其他一律拒绝。我在线上环境强制启用此配置审计时从未被指出 CORS 安全问题。4. 实操全流程从安装 Nginx 到上线验证避坑指南与性能调优4.1 不同系统安装 NginxUbuntu、CentOS、银河麒麟、离线环境实录Ubuntu 22.04推荐新版本默认源# 更新源并安装 sudo apt update sudo apt install nginx -y # 启动并设开机自启 sudo systemctl start nginx sudo systemctl enable nginx # 验证 curl -I http://localhost # 应返回 HTTP/1.1 200 OKCentOS 7/8注意CentOS 8 已停更建议用 Stream# CentOS 7 sudo yum install epel-release -y sudo yum install nginx -y # CentOS 8 Stream sudo dnf install nginx -y # 启动 sudo systemctl start nginx sudo systemctl enable nginx银河麒麟 V10国产化环境银河麒麟基于 Ubuntu但源不同。官方提供.deb包# 下载需注册麒麟软件商店获取链接 wget https://download.kylinos.cn/software/enterprise/kylin-nginx_1.20.1-1_amd64.deb # 安装依赖 sudo apt install libpcre3 libssl1.1 -y # 安装 Nginx sudo dpkg -i kylin-nginx_1.20.1-1_amd64.deb sudo apt --fix-broken install -y # 解决依赖 # 启动 sudo systemctl start nginx离线环境CentOS 8 / Ubuntu ARM64离线安装的核心是提前在联网机器上下载所有 RPM/DEB 包及其依赖。以 CentOS 8 为例# 在联网机器上相同系统版本 yum install yum-utils -y yumdownloader --resolve nginx # 会下载 nginx.rpm 及所有依赖如 pcre、openssl、zlib # 将所有 .rpm 文件拷贝到离线机器 scp *.rpm useroffline:/tmp/nginx-packages/ # 在离线机器上安装 cd /tmp/nginx-packages sudo rpm -Uvh *.rpm实操心得离线安装最大的坑是glibc版本不兼容。务必确认离线机器的glibc --version和下载包编译时的版本一致。我曾在一个 aarch64 银河麒麟环境因glibc 2.28和包要求的2.32不匹配折腾两天才发现要换用麒麟官方提供的交叉编译版 Nginx。4.2 配置文件结构与热加载如何安全修改不中断服务Nginx 配置不是改完就生效的。正确流程是语法检查必做sudo nginx -t # 输出应为nginx: the configuration file /etc/nginx/nginx.conf syntax is ok # nginx: configuration file /etc/nginx/nginx.conf test is successful如果报错nginx -t会精确指出哪一行、哪个文件出错。绝不跳过这步否则nginx -s reload会失败导致服务中断。热加载不重启零中断sudo nginx -s reload # 等价于sudo systemctl reload nginxreload的原理是Nginx 主进程 fork 新 worker 进程用新配置处理新连接老 worker 进程处理完已有连接后优雅退出。整个过程毫秒级用户无感知。配置文件组织规范主配置/etc/nginx/nginx.conf只保留全局设置user、worker_processes、events、http站点配置/etc/nginx/conf.d/*.conf每个项目一个文件如app.conf、admin.conf模块配置/etc/nginx/modules-enabled/存放第三方模块如headers-more日志目录/var/log/nginx/确保nginx用户有写权限。注意include /etc/nginx/conf.d/*.conf;这行必须在http块内否则conf.d下的文件不会被加载。我见过三次线上事故都是因为手抖把include写到了http外面导致所有站点 502。4.3 上线验证四步法确保 CORS 真正生效配置完不能只信curl要模拟真实浏览器行为检查响应头Chrome DevTools打开 F12 → Network → 刷新页面 → 找到一个/api/xxx请求点击该请求 → Headers → Response Headers确认存在Access-Control-Allow-Origin: https://app.example.com、Access-Control-Allow-Credentials: true如果是预检请求检查Access-Control-Allow-Methods等头。验证预检是否被拦截在 Console 执行fetch(/api/test, { method: POST, credentials: include, headers: { Authorization: Bearer xxx } })观察 Network 面板应该先出现OPTIONS请求Status 204再出现POST请求Status 200如果只有POST且报 CORS 错误说明预检没走通检查if ($request_method OPTIONS)块是否生效。测试 Credentials 是否生效登录后检查请求是否自动带CookieRequest Headers → Cookie后端返回的Set-Cookie是否被浏览器接收Application → Cookies如果 Cookie 没带上检查withCredentials: true是否在前端代码中设置且Access-Control-Allow-Credentials: true是否返回。压测验证稳定性用ab或wrk模拟并发# 模拟 100 并发持续 30 秒 ab -c 100 -t 30 https://app.example.com/api/health # 观察 Nginx error.log 是否有 timeout 或 upstream 错误4.4 性能调优让 Nginx 在高并发下依然稳如泰山默认配置适合小流量大流量需调整参数默认值生产建议原因worker_processesautoauto推荐或 CPU 核数每个 worker 是单线程auto会自动设为 CPU 核数worker_connections5124096~16384每个 worker 能处理的最大连接数ulimit -n需同步调高keepalive_timeout6515~30HTTP Keep-Alive 超时太长占连接太短增开销client_max_body_size1m根据业务设如上传文件设 100m防止大文件耗尽内存gzip onoffon配gzip_types减少传输体积提升首屏速度调整后记得检查系统限制# 查看当前 ulimit ulimit -n # 临时提高重启失效 sudo ulimit -n 65536 # 永久提高编辑 /etc/security/limits.conf # * soft nofile 65536 # * hard nofile 65536实操心得我们一个日活 50 万的项目worker_connections设为 8192keepalive_timeout设为 20搭配upstream的max_fails3在 3000 QPS 下 CPU 稳定在 30%从未因 Nginx 瓶颈导致 API 延迟上升。5. 常见问题排查与独家避坑技巧那些文档里不会写的真相5.1 典型问题速查表现象可能原因排查命令解决方案浏览器报No Access-Control-Allow-Origin headeradd_header未加always且后端返回 4xx/5xxcurl -I http://localhost/api/401加always参数或用more_set_headers模块OPTIONS请求返回 405 Method Not AllowedNginx 未拦截 OPTIONS直接转发给后端curl -X OPTIONS -I http://localhost/api/test添加if ($request_method OPTIONS) { return 204; }前端fetch带credentials: true但 Cookie 不发送Access-Control-Allow-Origin是*或未返回curl -I -H Origin: https://app.example.com http://localhost/api/test确保add_header用$http_origin且Access-Control-Allow-Credentials: trueNginx 启动失败提示bind() to 0.0.0.0:80 failed80 端口被占用如 Apache、Dockersudo ss -tulpn | grep :80sudo kill -9 PID或改用 8080 端口proxy_pass后端返回 502 Bad Gateway后端服务未启动或网络不通curl -I https://backend.internal:8000/health检查后端状态、防火墙、DNS 解析X-Real-IP总是127.0.0.1Nginx 和后端在同一台机器且用了localhostproxy_set_header X-Real-IP $remote_addr;确保proxy_set_header在location块内且$remote_addr是客户端真实 IP5.2 独家避坑技巧来自 12 个线上项目的血泪总结坑 1add_header的继承陷阱Nginx 的add_header指令**