本节目录
HTTPS
HTTPS 是 HTTP 的安全版本,用于在客户端与服务器之间安全地传输数据,它通过在 HTTP 协议之上加入 SSL/TLS 加密层,确保通信的机密性、完整性和身份认证。
HTTPS 核心由三部分组成:
- HTTP 应用层协议,负责网页内容的传输;
- SSL/TLS 安全协议层,提供加密、身份验证、完整性校验;
- 由权威机构(CA)签发的数字证书,证明服务器身份。
HTTPS 的几大优势:
- 数据加密可以防止敏感信息(如密码、银行卡等)泄露;
- 数字证书能确保你访问的是正确的网站,而不是骗子网站;
- SEO 友好,Google 等搜索引擎会优先收录 HTTPS 网站;
- 现代网页的一些功能,如位置信息、service worker、pwa 等必须 HTTPS。
HTTPS 握手过程如下图所示:

基于 RSA 传统握手流程(现已几乎淘汰)

基于 TLS1.3 首次握手流程(现代主流)

基于 TLS1.3 会话恢复的握手流程
早期的 HTTPS 握手(如 TLS1.x、RSA 等)流程较为繁琐,而现代 TLS(尤其是 TLS1.2 增强模式和 TLS1.3) 对握手过程进行了大幅优化,显著提升了性能与安全性。TLS1.3 作为现代互联网的推荐标准,其在性能(1-RTT)和安全(强制前向安全)上有质的蜕变,强烈推荐使用,TLS1.2 可以用作兜底手段兼容老设备。
需要编译时添加配置
--with-http_ssl_module,需要使用 TLS1.2及以上,系统版本太低的话需要手动指定 openssl--with-openssl=xxxx。
综合配置: 针对生产级别的全站 HTTPS 综合配置案例:
nginx
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
location ^~ /.well-known/acme-challenge/ { # 配置证书自动续证,用于 Let's Encrypt 机构
root /var/www/certbot;
default_type "text/plain";
try_files $uri =404;
}
location / { # 所有其他请求强制跳转到 HTTPS
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # SSL 公钥证书配置
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # SSL 私钥证书配置
ssl_protocols TLSv1.2 TLSv1.3; # 启用现代 TLS 协议(禁用不安全的 SSLv3/TLS1.0/TLS1.1)
# 使用 Mozilla 推荐的现代兼容加密套件,针对 TLS1.3 无用,因为 openssl 会自动选择
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384;
# 让客户端选择加密套件(TLS1.3 强制,TLS1.2 下推荐 off 以适应不同客户端硬件)
ssl_prefer_server_ciphers off;
# 启用 tls 会话缓存,提升响应和性能(对 TLS1.3 几乎无用,TLS1.3 使用的是 session ticket(PSK)机制)
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets on;
# 以下三个 key 需要编写自动化脚本定时轮换(nginx 不会自动轮询和重载它们)
# key.0 是正在用于加密的 key,后面俩是用于解密旧会话的,自动化脚本轮回过程是 new_key -> 0 -> 1 -> 2 -> 剔除
ssl_session_ticket_key /etc/nginx/ticket_keys/ticket.key.0; # 设置三个 ticket_key,防止历史会话被破解,有私钥也没用
ssl_session_ticket_key /etc/nginx/ticket_keys/ticket.key.1; # 通过 openssl rand 48 > /xxx/ticket.key.0 创建 key
ssl_session_ticket_key /etc/nginx/ticket_keys/ticket.key.2;
# 服务器代替浏览器查询证书状态,避免伪造
ssl_stapling on;
ssl_stapling_verify on;
resolver 223.6.6.6 8.8.8.8 valid=3600s;
resolver_timeout 5s;
# 强制浏览器未来至少 1 年时间都走 HTTPS 请求
# 警告:不要盲目开启,除非完全确认未来所有子域名都会永久使用 HTTPS
# 警告:如果未来想把某个子域名切回 HTTP 或者证书出问题了,用户将完全无法访问你的网站,且申请移除列表需要数周甚至数月
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# 增加安全头,防点击劫持、XSS、MIME嗅探、跨站劫持、跨站窗口攻击等
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
# 针对现代浏览器模型
add_header X-XSS-Protection "0" always;
add_header Cross-Origin-Opener-Policy "same-origin" always; # same-origin 和 same-site 可能会影响跨站 iframe、第三方资源嵌入,建议仅在主站启用
add_header Cross-Origin-Resource-Policy "same-site" always;
...
}
https://www.ssllabs.com/ssltest此网站可以测试你的 HTTPS 服务器的评级,A+为最好评级
HTTP/2 & HTTP/3
HTTP/2 和 HTTP/3 是 HTTP 协议的两个重大演进版本,旨在解决 HTTP/1.1 的性能瓶颈,如队头阻塞、高延迟、低效连接。
HTTP/1 有哪些问题???
- 在同一 TCP 连接上,所有请求必须按顺序处理,一个慢请求会阻塞后续请求;
- 多连接开销巨大,HTTP/1 是并行下载,浏览器需打开 6~8 个 TCP 连接,增加延迟和服务器负担;
- HTTP/1 的每个请求都携带 cookie、user-agent 等冗余头部,浪费带宽。
总结就是同条件下用 HTTP/1 网页加载更慢,高延时网络更甚。
HTTP/2 协议
随着网页变得越来越复杂(脚本多、图片大),HTTP/1.1 用起来已经捉襟见肘了,2009 年 Google 实验室为了提高网页加载速度,开发了试验性协议 SPDY,它引入了多路复用、头部压缩等技术,这显著减少了加载时间。于是 SPDY 协议在 chrome 等浏览器中率先获得支持,之后 IETF 的 HTTPbis 工作组也就决定以 SPDY 为基础制定新一代标准,制定了 HTTP/2 标准,并于 2015 年 5 月正式发布 HTTP/2。在 HTTP/2 标准确立后,google 也随即宣布不再维护 SPDY,全面拥抱 HTTP/2。
相比于 HTTP/1.1,HTTP/2 有哪些实质性的改进呢???
- 采用二进制分帧技术,将信息分割为更小的帧并采用二进制格式编码;
- 采用多路复用技术,在同一个 TCP 连接里,可以同时发送多个请求和响应,多个请求和响应交错传输,互不阻塞;
- 采用头部压缩技术,压缩重复的 HTTP Header,减少了 80% 以上的头部体积,极大提高了有效数据的传输量;
- 采用服务器推送技术,服务端可以预测客户端需要哪些资源并主动推送,减少了客户端请求的往返时间。(此技术因实现复杂、易滥用,主流浏览器已逐步废弃或默认关闭(chrome 和 edge 已移除,firefox 默认关闭,safari 支持有限))
注意:虽然 HTTP/2 标准本身不强制要求加密,但实际上 chrome、firefox 等主流浏览器都明确表示只支持基于 TLS 的 HTTP/2,因此现在的 HTTP/2 基本上是和 HTTPS 绑定使用的。
需要编译时添加配置--with-http_v2_module。
HTTP/2 配置示例:
nginx
listen 443 ssl http2; # 只需要在监听指令上加上 http2 关键字即可
虽然 HTTP/2 已经很流弊了,但还是有部分 HTTP1.1 的问题并没解决:
- 虽解它对应用层进行了大刀阔斧的改变,但底层还是基于 TCP 协议的,所以若某个数据包丢失,那么整个连接都会暂停等待重传,这也是 TCP 底层机制所决定的;
- 由于它依然运行在 TCP 之上,所以每次建立连接都意味着要经历漫长的握手等待,即便现在使用了 Fast Open 等优化技术,但也需要 2 ~ 3 个往返时间才能开始传输真正的数据;
- HTTP/2 虽然允许给不同的资源(如 css 优先于图片)设置优先级权重,但并没有规定服务器必须如何执行,很多服务器端(包括 nginx)和中间件对优先级的实现各不相同,甚至干脆忽略;
- 原本被寄予厚望的 "服务器主动推送",在实际应用中却并不理想,开发者很难精准判断什么时候该推、推什么。所以目前很多浏览器已经逐步废弃或限制这一功能了。
HTTP/3 协议
如果说上面的 HTTP/2 是对旧协议的 "大修",那么 HTTP/3 就是一场 "推倒重来" 的革命,它最根本的变化是放弃了传输层的 TCP 协议,改用基于 UDP 的 QUIC 协议。其协议的前身是 Google 于 2012 年发布的 gQUIC,但直到 2021 年 QUIC 才完成标准化,2022 年 6 月才正式发布此协议。
截至目前,根据 Cloudflare / Google 的公开数据,HTTP/3 请求占比约 20% 左右,支持站点占比接近 40%(且持续增长中)。
既然说 HTTP/3 是推倒重来的革命性进步,那么进步在哪了??
- 采用独立流传输,每个 HTTP 流在 QUIC 中是独立的,丢包只影响该流。这在 HTTP/2 中一个丢包整个连接都会卡死;
- 0-RTT 合并握手,建立连接只需 1 个 RTT,再次连接甚至可以 0-RTT。HTTP/2 中至少两三次才能建立连接;
- 连接无感迁移,使用 "连接 ID" 而非 IP 识别用户,ip 变更时连接不断,用户无感。HTTP/2 切换网络 ip 时,连接必须断开重连。
- TLS1.3 直接集成到 QUIC,无需单独握手;
- QUIC 运行在用户态,无需 TCP 那样为了升级协议还需要先升级内核,所以迭代和安全修复更快。
需要编译时添加配置
--with-http_v3_module。
HTTP/3 配置示例:( nginx-1.25.0 开始支持)
nginx
# 如果存在特殊需求或者使用的是 nginx-1.25 一下版本,需要使用 --with-http_v3_module 编译 quic 功能
# nginx-1.25 及之后的主线版本,已将此功能合并进主线,开箱即可用
server {
listen 443 quic reuseport; # udp 监听,用于 HTTP/3,reuseport 允许多个 worker 进程同时监听 udp443,对 quic 性能几乎是必写项
listen 443 ssl http2; # tcp 监听,兼容 HTTP/2
http3 on;
add_header Alt-Svc 'h3=":443"; ma=86400'; # 告诉浏览器优先选择 HTTP/3(可不写,浏览器会自动降级)
}
https://http3check.net可以检测一个网站是否开启了 HTTP/3 协议。
使用场景:
- HTTP/3(面向未来的高性能协议):高延迟或不稳定网络、实时应用、边缘 CDN、pwa和移动 web 应用、要求低延时等;(必须依赖 CDN 才能体现出优势,单机自建的 HTTP/3 在公网下的丢包收益很有限)
- HTTP/2(当前主流,高性价比选择):企业官网、博客电商、saas 应用、应用后端 api、传统 CDN、TLS 加密加性能提升等;
- HTTP/1.1(兼容性优先的兜底协议):老旧客户端、内部系统/API、调试开发、CDN 回源等(服务器通常会自动保留 HTTP/1.1 作为兜底)。
建站选择:HTTP/3 + HTTP/2 + HTTP/1.1 组合模式(因部分企业网络、校园网和防火墙是直接封了 UDP443 的,所以需要三协议组合使用)
- HTTP/3 作为先锋,为现代浏览器、移动端、网络波动环境(极速首屏加载)提供服务;
- HTTP/2 作为中坚力量,为主流的 PC 环境、不支持 UDP 的办公网络提供服务;
- HTTP1.1 充当兜底手段,为爬虫、老旧 api 调用工具(如 curl 旧版)、IE 等极老浏览器提供兜底服务。
HTTP 变量
-
ngx_http_map_module 变量映射:根据一个变量的值,动态生成另一个变量;
nginxmap $source_var $target_var { # 基本用法 default val1; case1 val2; ~regex val3; } map $http_user_agent $cache_time { # 示例,根据 user-agent 设置缓存时间 default 5m; ~*mobile 1m; ~*bot|spider 0s; } location ~* \.css$ { expires $cache_time; } -
ngx_http_referer_module 防盗链:根据 referer 请求头,限制资源被其他网站盗用(图片、视频、JS/CSS 等);
nginxvalid_referers [none|blocked|server_names] ...; # 基本用法 if ($invalid_referer) { ... } location ~* \.(jpg|jpeg|png|gif|mp4)$ { # 示例 valid_referers none blocked example.com *.example.com; # 允许空 Referer(直接访问)或来自本站 if ($invalid_referer) { rewrite ^/.*$ /nohotlink.png last; } } -
ngx_http_geo_module ip 段匹配:根据 $remote_addr 匹配 IP 段,设置变量(常用于内网判断);
nginxgeo $remote_addr $user_group { # 访问控制,动态分发 default A; 127.0.0.1 B; 192.168.1.0/24 B; 10.0.0.0/8 B; } server { if ($user_group = "B") { # 允许特定用户访问此处 ... } } -
ngx_http_geoip2_module 地域识别:根据客户端 ip 查询国家、城市、ASN 等信息;
这是第三方模块,需要自己编译添加进去
--add-module=/path/to/ngx_http_geoip2_modulenginxhttp { ... geoip2 /etc/nginx/GeoLite2-Country.mmdb { $geoip2_data_country_code source=$remote_addr country iso_code; $geoip2_data_country_name source=$remote_addr country names en; } geoip2 /etc/nginx/GeoLite2-City.mmdb { $geoip2_data_city source=$remote_addr city names en; } server { if ($geoip2_data_country_code = "JP") { # 屏蔽特定国家 return 403; } log_format geo '$remote_addr - $geoip2_data_country_code $geoip2_data_city [$time_local] "$request"'; # 记录地域访问日志 access_log /var/log/nginx/access_geo.log geo; } }应用场景:地域限流或屏蔽、多语言跳转、合规化处理等。(数据库下载处:
https://dev.maxmind.com/geoip/geolite2-free-geolocation-data/,需要注册 MaxMind 账号获取 license key 才能下载) -
ngx_http_split_clients_module 流量分割:基于 IP 或变量的哈希值,将流量按比例分配到不同后端或配置;
nginxsplit_clients $variable $target_var { # 基本用法 10% "group_a"; # 10% 的流量走 A 组 20% "group_b"; * "group_c"; # 最后 70% 流量走 C 组 } split_clients "${remote_addr}" $new_version { # 示例 5% "on"; * "off"; } server { location / { root /var/www/oldVersion; if ($new_version = "on") { root /var/www/newVersion; # 让 5% 的流量走新版本 } } }应用场景:AB 测试、灰度测试、性能比较等。
-
headers-more-nginx-module 增强 Header 操作:更灵活地操作请求和响应头;
这是第三方模块,需要自己编译添加进去
--add-module=/xxx/headers-morenginxlocation / { more_set_headers 'X-Frame-Options: DENY'; # 设置响应头 more_set_headers -t 'text/html' 'X-Is-Html: True'; # 针对特定类型设置响应头 more_set_headers -s '403 404' 'X-Error-Notice: Page-Not-Found'; # 根据指定状态码设置响应头 more_clear_headers 'X-Powered-By'; # 清除响应头 more_set_input_headers "User-Agent: Mozilla/5.0 (Compatible Device)"; # 修改请求头 more_clear_input_headers "X-Forwarded-For-Secret"; # 清除请求头 }适用场景:安全加固、统一 CORS 处理、后端服务解耦、请求伪装等(模块下载地址:
https://github.com/openresty/headers-more-nginx-module) -
ngx_http_lua_module:嵌入 lua 脚本,完全控制变量生命周期;
nginxlocation / { access_by_lua_block { # 编写 lua 脚本 local token = ngx.var.http_authorization if not validate_jwt(token) then ngx.exit(403) end ngx.var.user_id = extract_user_id(token) } proxy_set_header X-User-ID $user_id; }nginx 添加 openresty 毕竟麻烦,推荐直接使用 openresty 服务器:
https://openresty.org/cn/,它是基于 nginx + lua 开发的 web 服务器(可连接 mysql、redis 等数据库,可直接在服务器上写业务),在一些场景(广告竞价、秒杀系统等)下性能非常强大。
如果你非要作为模块使用 nginx,这是模块网址:
https://github.com/openresty/lua-nginx-module,这么做其实等于使用上面所说的 openresty 服务器。
HTTP 过滤
nginx 的 过滤模块是其处理 HTTP 响应内容的关键组件,它们工作在上游响应返回后、发送给客户端前的阶段,用于修改、压缩、缓存、记录或丢弃响应体/头部。其与处理器模块(proxy_pass、fastcgi_pass等)不同,过滤模块是链式调用的,每个模块处理完后传递给下一个,最终形成完整响应。
-
ngx_http_sub_filter_module 响应体文本替换:动态替换响应体中的字符串;
默认不开启,需手动编译
--with-http_sub_module,或动态编译进入。nginxlocation / { sub_filter_types text/html text/css; # 默认只支持 text/html,这里扩展为支持 css sub_filter 'aaa' 'bbb'; # 将 aaa 替换成 bbb sub_filter_once off; # off 全局替换,on (默认)只替换第一个匹配项 sub_filter_last_modified on; # 更新 Last-Modified 头 }- 该模块不支持正则,该模块只能进行固定的字符串替换。如果需要使用正则表达式,可以选择第三方模块
ngx_http_substitutions_filter_module。 - 该模块会关闭缓冲(
proxy_buffering off),影响性能。
- 该模块不支持正则,该模块只能进行固定的字符串替换。如果需要使用正则表达式,可以选择第三方模块
-
ngx_http_image_filter_module 图片动态处理:服务器端动态处理图片,可以缩放、裁剪、旋转、生成缩略图;
默认不开启,需手动编译
--with-http_image_filter_module,或动态编译进入;对于包管理器版本来说,需要
dnf install nginx-extras -y安装 extras。nginxlocation /img/ { # 常见用法 image_filter resize 300 200; # 缩放 image_filter resize width height; # 等比例缩放 image_filter crop 150 150; # 裁剪 image_filter crop width height; # 强制缩放并裁剪至 exact 尺寸,resize 和 crop 不能同时使用 image_filter rotate 90; # 旋转 image_filter jpeg_quality 90; # jpeg 质量 (1-100) image_filter_buffer 10M; # 最大处理内存 (默认 1m) image_filter_interlace on; # 启用渐进式 jpeg image_filter size; # json 格式返回图片尺寸(常用作 api) } location ~ ^/imgs/(\d+)x(\d+)/(.+\.(jpg|jpeg|png))$ { # 请求 /imgs/300x200/image.jpg,返回 300x200 缩略图 alias /var/www/images/$3; image_filter resize $1 $2; image_filter_jpeg_quality 85; image_filter_buffer 20M; expires 1M; # 告诉客户端浏览器,此资源缓存一个月(秒s、分m、小时h、天d、周w、月M、年y) }适用场景:中小型网站、内部系统、对格式要求不高的缩略图服务,更高级的图像(webp、水印等)需要专门的图像处理服务,此内置模块只支持 jpeg、gif、png(部分版本可能支持 webp)。
注意:- 此服务处理 10m 图片可能需要大于 100m 的内存,内存和 CPU 压力会很大;
- 为了防止被 dos 攻击,建议限制处理大小
if ($1 > 4096) { return 400; }; - 避免目录被恶意遍历,建议 location 写成
location ~ ^/img/[^/]+\.(jpg|png)$。
-
ngx_http_addition_module 前后缀注入:在响应体开头或结尾插入本地静态内容;
默认不开启,需手动编译
--with-http_addition_module,或动态编译进入。nginxlocation / { add_before_body /prefix.html; # 插入头部 add_after_body /suffix.html; # 插入尾部 addition_types text/html text/plain; # 指定哪些 MIME 类型文件可以执行插入(默认是 text/html ) }注意:
- 该模块默认不包含在 Nginx 中。编译时需要添加参数
--with-http_addition_module; - 如果主响应或被插入的子请求开启了压缩,该模块将无法工作,需要在对应的 location 中关闭压缩,或者确保后端不返回压缩内容;
- 该模块默认不包含在 Nginx 中。编译时需要添加参数
-
ngx_http_ssi_filter_module 服务器端包含 SSI:解析 HTML 中的 指令;
nginxlocation ~* \.shtml$ { ssi on; ssi_silent_errors on; ssi_types text/shtml; }注意:
- 这种类型的网页几乎已被现代前端构建工具取代,不推荐在新项目使用。
-
ngx_http_headers_filter_module 响应头操作:添加、修改响应头(最常用);
nginxlocation ~* \.css$ { expires 1M; add_header Cache-Control "public, immutable"; add_header X-Content-Type-Options nosniff; }注意:
- add_header 在子块中会覆盖父块!!若需继承,必须在子块中重新声明所有 header 设置;
- 因为此模块的局限性,推荐使用上述提到的
headers-more-nginx-module模块。
HTTP 压缩
-
ngx_http_gzip_module 压缩为 gz:
nginx 开源版默认不编译此模块,但几乎所有发行版(如 nginx-full、nginx-extras)或自行编译时都会启用;
触发条件:
- 客户端请求头包含 Accept-Encoding: gzip;
- 响应 Content-Type 在 gzip_types 列表中;
- 响应体长度大于了 gzip 的最小压缩长度。
nginxgzip on; # 启用 gzip gzip_vary on; # 添加 Vary: Accept-Encoding gzip_types text/css application/javascript; # 指定压缩的 mime 类型 gzip_min_length 1024; # 响应体大于此值才压缩(字节) gzip_comp_level 6; # 压缩级别(1-9,越高越慢) gzip_proxied any; # 对代理请求也压缩注意:
- 图片、视频、zip 等已压缩格式不要再 gzip。
-
ngx_http_gunzip_module 解压 gzip:上游返回 gzip 压缩内容后,客户端却不支持 gzip,nginx 将自动解压后再返回;
默认不开启,需手动编译
--with-http_gunzip_module,或动态编译进入。nginxlocation { gunzip on; ... } -
ngx_http_gzip_static_module 预压缩 gzip 文件处理:如果存在 .gz 后缀的预压缩文件(如 app.js.gz),且客户端支持 gzip,则直接返回该文件即可;
默认不开启,需手动编译
--with-http_gzip_static_module,或动态编译进入。nginxgzip_static on; -
ngx_brotli 压缩为 br:比 gzip 压缩率高 15%~30%,尤其适合文本(如 HTML/CSS/JS);
这是第三方模块,需要自己编译添加进去
--add-module=/xxx/ngx_brotli。nginxgzip on; brotli on; # gzip 和 brotli 共存,nginx 会根据 Accept-Encoding 字段自动选择最优格式 brotli_comp_level 6; # 压缩等级 brotli_types text/css application/javascript image/svg+xml; brotli_static on; # 支持预压缩的 .br 文件,功能和 gzip_static 相同 -
ngx_zstd 压缩为 zstd:获得比 gzip 更高的压缩率和更快的解压速度;(实验性)
这是第三方模块,需要自己编译添加进去
--add-module=/xxx/ngx_zstd。nginxzstd on; zstd_comp_level 6; zstd_types text/plain text/css application/json application/javascript text/xml application/xml image/svg+xml; zstd_min_length 1024; zstd_vary on;警告:
- zstd 压缩的支持现阶段不是很好,Chrome (123+)、Firefox (126+)、Edge 均已获得原生支持,目前唯独 Safari 缺席。
综合配置: 针对生产级别的压缩综合配置案例:压缩策略 zstd > brotli > gzip
nginx
http {
...
zstd on;
zstd_comp_level 3;
zstd_types text/plain text/css application/json application/javascript text/xml image/svg+xml;
brotli on;
brotli_static on; # 优先查找磁盘上的 .br 预压缩文件
brotli_comp_level 5; # 动态压缩级别不宜过高,5-6 差不多
brotli_types text/plain text/css application/json application/javascript text/xml image/svg+xml;
gzip on; # 兜底策略
gzip_static on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_types text/plain text/css application/json application/javascript text/xml image/svg+xml;
...
}
附加
看到了有些小伙伴还不会配置 nginx 跨域,这里是一份用 nginx 解决跨域的配置示例:(不依赖第三方模块,兼容性更好)
nginx
location /api/ {
# 动态判断 origin
set $cors_origin "";
if ($http_origin ~* "^http?://(localhost|.*\.example\.com)$") {
set $cors_origin $http_origin;
}
# 使用 add_header (后端返回 4xx/5xx,需要加 always 才能生效)
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, DELETE" always;
add_header Access-Control-Allow-Credentials "true" always;
add_header Access-Control-Allow-Headers "Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,X-Requested-With" always;
# 预检请求处理
if ($request_method = 'OPTIONS') {
add_header Access-Control-Max-Age 1728000 always;
add_header Content-Type "text/plain; charset=utf-8" always;
add_header Content-Length 0 always;
return 204;
}
proxy_pass http://backend;
}
HTTPS 的 session ticket key 的轮换脚本:
bash
#!/bin/bash
# Key 存放目录
KEY_DIR="/etc/nginx/ticket_keys"
# Key 文件前缀
KEY_PREFIX="ticket.key"
# OpenSSL 二进制路径
OPENSSL_CMD="openssl"
# Nginx 二进制路径 (用于检查配置)
NGINX_CMD="nginx"
# 系统服务管理命令 (用于重载 Nginx)
SYSTEMCTL_CMD="systemctl"
# 日志文件
LOG_FILE="/var/log/nginx/key_rotation.log"
# 记录日志
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}
# 检查目录是否存在
if [ ! -d "$KEY_DIR" ]; then
log "Error: Key directory $KEY_DIR does not exist."
exit 1
fi
cd "$KEY_DIR" || exit 1
# 开始轮换 (倒序操作,防覆盖),删除 key.2,key.1 -> key.2,key.0 -> key.1,生成新 key.0
# 移动 key.1 到 key.2 (如果 key.1 存在)
if [ -f "${KEY_PREFIX}.1" ]; then
mv "${KEY_PREFIX}.1" "${KEY_PREFIX}.2"
fi
# 移动 key.0 到 key.1 (如果 key.0 存在)
if [ -f "${KEY_PREFIX}.0" ]; then
mv "${KEY_PREFIX}.0" "${KEY_PREFIX}.1"
fi
# 生成新的 key.0,使用 openssl 生成 48 字节 (RFC 5077 推荐) 或 80 字节的随机数
$OPENSSL_CMD rand 48 > "${KEY_PREFIX}.0"
if [ $? -ne 0 ]; then
log "Error: Failed to generate new key using openssl."
exit 1
fi
# 设置权限,仅 root 可读写,保证私钥安全
chmod 600 "${KEY_PREFIX}".*
log "Keys rotated. New key generated."
# 检查 nginx 配置并重载,在重载前必须检查配置,防止因配置错误导致 nginx 挂掉
$NGINX_CMD -t > /dev/null 2>&1
if [ $? -eq 0 ]; then
# 配置无误,重载 Nginx
$SYSTEMCTL_CMD reload nginx
if [ $? -eq 0 ]; then
log "Nginx reloaded successfully."
else
log "Error: Failed to reload Nginx."
exit 1
fi
else
log "Error: Nginx configuration test failed. Not reloading."
exit 1
fi
exit 0