欢迎你来读这篇博客,这篇博客主要记录 Nginx 在实际 Web 项目中的常见使用方式,包括安装、核心配置、静态资源托管、反向代理、负载均衡、HTTPS、HTTP 自动跳转 HTTPS,以及 Docker、Kubernetes 两种部署场景。
如果只把 Nginx 理解成“放静态文件的 Web Server”,会低估它的价值。在实际项目中,它更常出现在系统入口层:浏览器先访问 Nginx,由 Nginx 负责 TLS/HTTPS、域名、静态资源、反向代理和流量转发,再把动态请求交给 Spring Boot、Tomcat、Node.js 等后端服务。
序言 什么是 Nginx Nginx 是一个高性能的 HTTP Server、反向代理服务器和通用 TCP/UDP 代理服务器。它采用事件驱动模型,适合处理大量并发连接,并且配置简单、资源占用相对较低,因此经常被部署在 Web 系统的最外层。
Nginx 能做什么 常见能力包括:
Web Server:直接提供 HTML、CSS、JavaScript、图片等静态资源。
反向代理:将客户端请求转发到 Spring Boot、Tomcat、Node.js 等后端应用。
负载均衡:在多个后端实例之间分配请求。
HTTPS/TLS 终止:统一处理证书和 TLS 握手,后端应用可以只提供 HTTP。
HTTP 自动跳转 HTTPS:将 80 端口请求统一重定向到 443。
动静分离:静态资源由 Nginx 直接处理,动态请求交给后端。
缓存:缓存后端响应,减少源站压力。
域名和虚拟主机:一台 Nginx 可以根据域名代理多个系统。
WebSocket 代理:支持 WebSocket、SSE 等长连接场景。
一个典型的生产请求链路 1 2 3 4 5 6 7 8 9 10 11 12 Browser / App | | HTTP :80 / HTTPS :443 v +-----------------------+ | Nginx | | TLS / Redirect / Proxy| +-----------------------+ | | | +------------------> Static Files | +--------------------------------> Spring Boot / Tomcat / API
在 Docker 中,Nginx 和 Web 应用通常是不同容器;在 Kubernetes 中,通常由 Gateway/Ingress 作为集群入口,Service 再把流量送到 Web Pod。
安装部署 Docker 方式安装 如果只是快速启动一个 Nginx:
1 2 3 4 5 6 docker run -d \ --name nginx \ --restart unless-stopped \ -p 80:80 \ -p 443:443 \ nginx:stable-alpine
查看默认配置:
1 docker exec -it nginx nginx -T
检查配置:
1 docker exec nginx nginx -t
重新加载配置:
1 docker exec nginx nginx -s reload
生产环境不建议只运行一个“裸 Nginx 容器”,而是应该将配置文件、证书和日志通过 volume 挂载,并且固定镜像版本或 digest,避免镜像标签变化导致不可预期升级。
Linux 软件包安装 在 Rocky Linux、RHEL、AlmaLinux 等系统上,可以优先使用系统软件包或 Nginx 官方仓库,而不是每次都从源码编译。
1 2 sudo dnf install -y nginxsudo systemctl enable --now nginx
查看版本和编译参数:
检查配置:
重新加载:
1 sudo systemctl reload nginx
源码编译安装 如果确实需要自定义模块,再考虑源码编译。
安装依赖:
1 sudo dnf install -y gcc make openssl openssl-devel zlib zlib-devel pcre2 pcre2-devel
解压源码后:
1 2 3 4 5 6 7 ./configure \ --prefix=/usr/local/nginx \ --with-http_ssl_module \ --with-http_stub_status_module make -j"$(nproc) " sudo make install
这里需要特别注意:配置脚本是 ./configure,不是 ./config。
如果通过源码编译 Nginx,并且要使用 HTTPS,需要启用 --with-http_ssl_module。大部分发行版提供的 Nginx 软件包以及官方 Docker 镜像已经具备 HTTPS 能力,不需要重新编译。
检查当前 Nginx 是否编译了 SSL 模块:
1 nginx -V 2>&1 | grep -- --with-http_ssl_module
Nginx 配置文件结构 Nginx 主配置文件通常是:
常见额外配置目录:
1 /etc/nginx/conf.d/*.conf
源码安装时通常位于:
1 /usr/local/nginx/conf/nginx.conf
Nginx 配置主要包含三层:
全局块:worker、PID、日志、运行用户等。
events:连接和事件模型配置。
http:HTTP Server、虚拟主机、反向代理、缓存、压缩等配置。
一个比较实用的基础配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 user nginx;worker_processes auto;worker_rlimit_nofile 65535 ;pid /var/run/nginx.pid;error_log /var/log/nginx/error .log warn ;events { worker_connections 4096 ; multi_accept on ; }http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local ] "$request " ' '$status $body_bytes_sent "$http_referer " ' '"$http_user_agent " "$http_x_forwarded_for " ' 'rt=$request_time urt=$upstream_response_time ' ; access_log /var/log/nginx/access.log main; sendfile on ; tcp_nopush on ; tcp_nodelay on ; keepalive_timeout 65 ; server_tokens off ; client_max_body_size 20M ; client_body_timeout 30s ; client_header_timeout 30s ; gzip on ; gzip_vary on ; gzip_proxied any; gzip_comp_level 5 ; gzip_min_length 1k ; gzip_types text/plain text/css application/json application/javascript application/xml image/svg+xml; include /etc/nginx/conf.d/*.conf ; }
worker_connections 并不等于整个 Nginx 的最大并发量。实际容量还受到 worker 数量、文件描述符、上游连接、Keep-Alive、操作系统网络参数和业务响应时间等因素影响。
Nginx 接入 HTTPS HTTPS 在 Nginx 中到底做了什么 最常见的架构是让 Nginx 做 TLS Termination:
1 2 3 4 5 6 7 8 9 Client | | HTTPS v Nginx :443 | | HTTP(内网) v Web Application :8080
这样证书只需要部署在 Nginx 或 Kubernetes Gateway 上,后面的 Spring Boot/Tomcat 不需要每个实例都配置证书。
如果安全要求更高,也可以让 Nginx 到后端之间继续使用 HTTPS,甚至使用 mTLS,但普通企业 Web 系统一般先从入口 TLS 终止开始。
HTTPS 的前置条件 正式部署 HTTPS 前至少需要:
一个域名,例如 example.com。
DNS 已经解析到服务器或负载均衡入口。
防火墙、安全组允许 80 和 443。
一张与域名匹配的证书。
Nginx 具备 SSL 模块。
对于公网生产环境,应使用受浏览器信任的 CA 签发证书,例如企业购买的证书或 ACME/Let’s Encrypt 等自动签发证书。
自签名证书适合开发、实验、内网测试。自签名不代表通信没有加密,但浏览器默认不信任签发者,因此会显示证书不受信任警告。
开发环境生成自签名证书 引用文章中使用 OpenSSL 生成了私钥、CSR 和自签名 CRT,这个思路可以用于测试,但不建议继续使用 1024 位 RSA。
测试环境可以直接生成 RSA 2048 自签名证书:
1 2 3 4 5 6 7 8 9 10 11 12 mkdir -p /etc/nginx/sslcd /etc/nginx/ssl openssl req -x509 \ -nodes \ -newkey rsa:2048 \ -sha256 \ -days 365 \ -keyout server.key \ -out server.crt \ -subj "/C=CN/ST=Shanghai/L=Shanghai/O=Example/OU=Dev/CN=example.com" \ -addext "subjectAltName=DNS:example.com,DNS:www.example.com"
生成:
1 2 /etc/nginx/ssl/server.key /etc/nginx/ssl/server.crt
私钥一定不要提交到 Git:
1 chmod 600 /etc/nginx/ssl/server.key
关于 CSR 如果证书由企业 CA 或公网 CA 签发,常见流程是:
1 2 3 4 5 openssl genrsa -out example.com.key 2048 openssl req -new \ -key example.com.key \ -out example.com.csr
将 CSR 交给 CA,最终获得证书链文件。
实际部署时,很多 CA 会直接提供类似:
1 2 fullchain.pem privkey.pem
其中 fullchain.pem 一般包含服务器证书和中间证书链,privkey.pem 是私钥。
最基础的 HTTPS 配置 创建:
1 /etc/nginx/conf.d/example.conf
配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 server { listen 443 ssl; listen [::]:443 ssl; server_name example.com www.example.com; ssl_certificate /etc/nginx/ssl/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3 ; ssl_session_cache shared:SSL:10m ; ssl_session_timeout 10m ; ssl_session_tickets off ; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri / =404 ; } }
如果使用前面的自签名证书,则改成:
1 2 ssl_certificate /etc/nginx/ssl/server.crt;ssl_certificate_key /etc/nginx/ssl/server.key;
测试配置:
重新加载:
1 sudo systemctl reload nginx
测试:
1 curl -I https://example.com
自签名证书可以临时使用:
1 curl -kI https://example.com
查看 TLS 握手和证书链:
1 2 3 4 openssl s_client \ -connect example.com:443 \ -servername example.com \ -showcerts
HTTP 自动跳转 HTTPS 只启用 443 并不会让用户访问 http://example.com 时自动进入 HTTPS。最稳妥的方式是单独保留一个监听 80 的 server,只负责重定向。
网站场景:301 跳转 1 2 3 4 5 6 7 8 server { listen 80 ; listen [::]:80 ; server_name example.com www.example.com; return 301 https://example.com$request_uri ; }
例如:
1 http://example.com/user?id=100
会变成:
1 https://example.com/user?id=100
$request_uri 会保留路径和 QueryString。
如果希望 www.example.com 和 example.com 都保持原 Host:
1 return 301 https://$host $request_uri ;
如果这是公网系统,并且 server_name 是固定域名,使用固定的规范域名通常更加清晰,也可以顺手完成 www 到主域名的统一。
API 场景:优先考虑 308 301 在不同客户端中可能涉及请求方法变化。对于需要保留 POST/PUT/PATCH 等请求方法和请求体的 API,永久跳转更适合使用 308:
1 2 3 4 5 6 server { listen 80 ; server_name api.example.com; return 308 https://api.example.com$request_uri ; }
因此可以简单理解:
普通网站:301 很常见。
API:更关注方法和请求体保持时,使用 308 更稳妥。
HTTPS + 反向代理 Web 应用 最常见的生产部署方式不是让 Nginx 自己返回页面,而是 Nginx 接收 HTTPS,再把请求代理到 Spring Boot/Tomcat。
假设后端地址:
配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 upstream web_backend { server 127.0.0.1:8080 ; }server { listen 80 ; server_name example.com; return 308 https://example.com$request_uri ; }server { listen 443 ssl; server_name example.com; ssl_certificate /etc/nginx/ssl/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3 ; location / { proxy_pass http://web_backend; proxy_http_version 1 .1 ; 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 ; proxy_set_header X-Forwarded-Host $host ; proxy_set_header X-Forwarded-Port $server_port ; proxy_connect_timeout 5s ; proxy_send_timeout 60s ; proxy_read_timeout 60s ; } }
此时调用链:
1 2 3 4 5 6 7 8 https://example.com | v Nginx :443 | | HTTP v Spring Boot :8080
浏览器看到的是 HTTPS,而内部 Web 应用只需要监听 8080。
X-Forwarded-* 为什么重要 TLS 在 Nginx 结束之后,后端真正收到的连接可能是:
如果不传递代理 Header,后端可能误以为原始请求就是 HTTP,导致:
OAuth2 回调地址生成成 http://。
Spring Security 重定向 URL 错误。
OpenAPI/Swagger Server URL 错误。
登录成功后跳转到错误协议。
获取不到客户端真实 IP。
因此建议至少传递:
1 2 3 4 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 ;
Spring Boot 项目如果需要根据这些 Header 正确识别原始协议,可以根据实际部署方式启用 Forwarded Header 支持,例如:
1 server.forward-headers-strategy =framework
前提是这些 Header 只能由可信反向代理注入,不能无条件信任来自公网客户端的伪造 Header。
proxy_pass 末尾 / 的坑这可能是 Nginx 最容易踩的坑之一。
配置一:
1 2 3 location /api/ { proxy_pass http://127.0.0.1:8080; }
请求:
后端收到:
配置二:
1 2 3 location /api/ { proxy_pass http://127.0.0.1:8080/; }
请求:
后端收到:
区别就是 proxy_pass 后面的 / 会影响 URI 替换规则。
WebSocket 代理 如果项目包含 WebSocket,可以在 http 块中增加:
1 2 3 4 map $http_upgrade $connection_upgrade { default upgrade; '' close; }
然后在对应 location 中:
1 2 3 4 5 6 7 8 9 10 11 12 13 location /ws/ { proxy_pass http://web_backend; proxy_http_version 1 .1 ; proxy_set_header Upgrade $http_upgrade ; proxy_set_header Connection $connection_upgrade ; proxy_set_header Host $host ; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ; proxy_set_header X-Forwarded-Proto $scheme ; proxy_read_timeout 3600s ; }
HSTS:让浏览器以后只访问 HTTPS 确认 HTTPS 已经稳定运行后,可以考虑启用 HSTS:
1 add_header Strict-Transport-Security "max-age=31536000" always;
如果所有子域名也永远支持 HTTPS,再考虑:
1 add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
HSTS 一旦被浏览器缓存,在有效期内浏览器会主动拒绝 HTTP。不要在 HTTPS 还没有完全验证、部分子域名还需要 HTTP、或者证书管理流程不稳定时贸然启用 includeSubDomains 或 preload。
Docker 场景:Nginx + Web 应用如何部署 在 Docker 中,一个非常推荐的方式是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Internet | 80 / 443 | v +----------------+ | nginx container| +----------------+ | | Docker Network v +----------------+ | web container | | :8080 | +----------------+
只有 Nginx 暴露 80/443,Web 应用不直接暴露到公网。
目录结构 1 2 3 4 5 6 7 8 nginx-web/ ├── docker-compose.yml └── nginx/ ├── conf.d/ │ └── web.conf └── ssl/ ├── fullchain.pem └── privkey.pem
docker-compose.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 services: web: image: registry.example.com/example-web:1.0.0 restart: unless-stopped expose: - "8080" networks: - app-network nginx: image: nginx:stable-alpine restart: unless-stopped depends_on: - web ports: - "80:80" - "443:443" volumes: - ./nginx/conf.d:/etc/nginx/conf.d:ro - ./nginx/ssl:/etc/nginx/ssl:ro - nginx-logs:/var/log/nginx networks: - app-network networks: app-network: driver: bridge volumes: nginx-logs:
这里故意不给 web 配置:
因为正常情况下外部用户不应该绕过 Nginx 直接访问 Web 容器。
expose 只是表达容器内部服务端口,容器之间通过 Compose 网络通信。
Docker 中的 Nginx 配置 nginx/conf.d/web.conf:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 upstream web_backend { server web:8080 ; }server { listen 80 ; server_name example.com; return 308 https://example.com$request_uri ; }server { listen 443 ssl; server_name example.com; ssl_certificate /etc/nginx/ssl/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3 ; client_max_body_size 20M ; location / { proxy_pass http://web_backend; proxy_http_version 1 .1 ; 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 ; proxy_connect_timeout 5s ; proxy_read_timeout 60s ; } }
这里最重要的是:
web 是 Docker Compose 的 Service Name。Nginx 通过 Docker 网络 DNS 找到 Web 容器。
不要写:
因为在 Nginx 容器中,127.0.0.1 指向的是 Nginx 容器自己,而不是 Web 容器。
启动:
查看:
检查 Nginx 配置:
1 docker compose exec nginx nginx -t
重新加载:
1 docker compose exec nginx nginx -s reload
日志:
1 2 3 docker compose logs -f nginx docker compose logs -f web
Docker 场景:前端 SPA + 后端 API 对于 React/Vue 等前后端分离项目,也经常让 Nginx 同时负责:
/:前端静态资源。
/api/:代理后端 API。
配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 upstream api_backend { server api:8080 ; }server { listen 80 ; server_name example.com; return 308 https://example.com$request_uri ; }server { listen 443 ssl; server_name example.com; ssl_certificate /etc/nginx/ssl/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3 ; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri / /index.html; } location /api/ { proxy_pass http://api_backend; 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 ; } location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|woff2?)$ { expires 7d ; add_header Cache-Control "public" ; try_files $uri =404 ; } }
对于 SPA 项目:
1 try_files $uri $uri / /index.html;
非常重要,否则访问:
1 https://example.com/user/100
并刷新页面时,Nginx 会尝试寻找 /user/100 文件,从而返回 404。
Docker 场景常见问题 1. 容器中不要使用 127.0.0.1 找另一个容器 错误:
1 proxy_pass http://127.0.0.1:8080;
正确:
1 proxy_pass http://web:8080;
2. 不要把私钥打进镜像 不要:
1 COPY privkey.pem /etc/nginx/ssl/
生产环境应使用 Secret、volume、外部证书系统或发布平台注入。
3. 后端端口尽量不要直接暴露公网 如果所有请求都必须经过 Nginx,就不要额外映射:
否则攻击者可能绕开 Nginx 的 TLS、鉴权、WAF、限流等入口规则。
4. 修改配置后先 nginx -t 1 docker compose exec nginx nginx -t
确认成功后再 reload。
Kubernetes 场景:Web 项目应该怎么部署 Kubernetes 中不要简单照搬 Docker Compose 的思路,在每个 Web Pod 前面再塞一个独立入口 Nginx。
更常见的结构是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Internet | v LoadBalancer / Gateway | | TLS Termination v Gateway / Route | v Service (ClusterIP) | +----------+----------+ | | | v v v Web Pod Web Pod Web Pod :8080 :8080 :8080
如果是 React/Vue 静态前端,前端本身仍然可以用一个 Nginx 容器来提供静态文件,但外部 HTTPS/TLS 最好统一由 Kubernetes 的 Gateway/入口层处理。
先部署 Web Deployment 以下以 Spring Boot Web 服务为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 apiVersion: apps/v1 kind: Deployment metadata: name: example-web namespace: web-prod spec: replicas: 3 revisionHistoryLimit: 5 selector: matchLabels: app: example-web template: metadata: labels: app: example-web spec: containers: - name: web image: registry.example.com/example-web:1.0.0 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 8080 env: - name: TZ value: Asia/Shanghai readinessProbe: httpGet: path: /actuator/health/readiness port: http initialDelaySeconds: 10 periodSeconds: 10 timeoutSeconds: 3 failureThreshold: 3 livenessProbe: httpGet: path: /actuator/health/liveness port: http initialDelaySeconds: 30 periodSeconds: 20 timeoutSeconds: 3 failureThreshold: 3 resources: requests: cpu: "250m" memory: "512Mi" limits: cpu: "2" memory: "1Gi"
这里的健康检查路径是 Spring Boot Actuator 示例,需要应用实际启用对应 Probe;如果不是 Spring Boot,改成项目真实存在的健康检查路径。
创建 ClusterIP Service 1 2 3 4 5 6 7 8 9 10 11 12 13 apiVersion: v1 kind: Service metadata: name: example-web namespace: web-prod spec: type: ClusterIP selector: app: example-web ports: - name: http port: 80 targetPort: http
此时集群内部可以通过:
1 http://example-web.web-prod.svc.cluster.local
访问应用。
外部不需要直接访问 Pod IP,也不需要给每个 Pod 配公网端口。
Kubernetes HTTPS:TLS Secret 如果已经有证书:
1 2 3 4 kubectl create secret tls example-com-tls \ --cert=fullchain.pem \ --key=privkey.pem \ -n web-prod
查看:
1 kubectl get secret example-com-tls -n web-prod
Secret 应该由 Kubernetes RBAC、GitOps Secret 管理方案、Vault 或其他 Secret 管理系统保护,不要把私钥明文写入 Git 仓库。
Kubernetes 入口选择:2026 年的新项目优先 Gateway API 过去大量教程会直接安装社区版 ingress-nginx,然后创建 Ingress 资源。
需要注意的是:Kubernetes 社区的 Ingress NGINX 项目已经在 2026 年 3 月退役 ,退役后不会再提供新的 bugfix 和安全更新。与此同时,Kubernetes 官方文档已经明确推荐使用 Gateway API 替代 Ingress 作为新项目的演进方向。
这里需要区分三个概念:
1 2 3 4 Ingress API Kubernetes 的 API 资源,仍然存在,但 API 已冻结 Ingress Controller 实现 Ingress 的控制器,有多种产品 Ingress NGINX Kubernetes 社区过去维护的一种 Ingress Controller,已退役 Gateway API Kubernetes 新一代流量入口 API
所以:
老集群:可以继续维护现有 Ingress,但应评估控制器迁移。
新项目:优先考虑 Gateway API。
具体 GatewayClass 由你的集群网络/网关实现决定。
可以先查看集群已有 GatewayClass:
1 kubectl get gatewayclass
Kubernetes Gateway API:HTTPS + HTTP 自动跳 HTTPS 下面给出一个通用 Gateway API 示例。
前提:
集群已经安装 Gateway API CRD。
已经部署一个支持 Gateway API 的 Controller。
kubectl get gatewayclass 能看到可使用的 GatewayClass。
已经创建 example-com-tls TLS Secret。
假设 GatewayClass 名称为:
请根据自己的集群替换。
Gateway 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: example-gateway namespace: web-prod spec: gatewayClassName: example-gateway-class listeners: - name: http protocol: HTTP port: 80 hostname: example.com allowedRoutes: namespaces: from: Same - name: https protocol: HTTPS port: 443 hostname: example.com tls: mode: Terminate certificateRefs: - kind: Secret group: "" name: example-com-tls allowedRoutes: namespaces: from: Same
这里 HTTPS 的 TLS 会在 Gateway 入口终止,后面的 Service 仍然可以使用普通 HTTP。
HTTP -> HTTPS 自动跳转 创建一个只绑定 http listener 的 HTTPRoute:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-http-redirect namespace: web-prod spec: parentRefs: - name: example-gateway sectionName: http hostnames: - example.com rules: - filters: - type: RequestRedirect requestRedirect: scheme: https statusCode: 301
效果:
1 http://example.com/user/100
会跳到:
1 https://example.com/user/100
Gateway API 也支持 307/308 等方法保持型重定向,但具体实现需要检查 Gateway Controller 的支持特性。普通网站使用 301 已经足够;API 场景需要严格保持请求方法时,再确认 Controller 支持 308 后使用 308。
HTTPS 请求转发到 Web Service 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-web namespace: web-prod spec: parentRefs: - name: example-gateway sectionName: https hostnames: - example.com rules: - matches: - path: type: PathPrefix value: / backendRefs: - name: example-web port: 80
最终链路:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Browser | | HTTP :80 +----------------> HTTPRoute Redirect -> HTTPS | | HTTPS :443 v Gateway | | HTTP v Service example-web:80 | v Pod :8080
Kubernetes:前端和后端同时部署 如果系统是:
1 2 3 4 5 / -> frontend /api/ -> backend
可以准备两个 Service:
1 2 frontend-service:80 backend-service:80
然后让 Gateway 统一路由:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: example-app namespace: web-prod spec: parentRefs: - name: example-gateway sectionName: https hostnames: - example.com rules: - matches: - path: type: PathPrefix value: /api/ backendRefs: - name: backend-service port: 80 - matches: - path: type: PathPrefix value: / backendRefs: - name: frontend-service port: 80
这样不需要额外在外部再部署一层 Nginx 做路径分发。
如果 frontend-service 本身是一个 Nginx Pod,它只负责静态资源即可;TLS、域名和公网入口由 Gateway 负责。
Kubernetes 旧方案:Ingress 怎么写 Ingress API 本身并没有被删除,已有集群仍然大量使用。
一个基础 HTTPS Ingress:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-web namespace: web-prod spec: ingressClassName: your-ingress-class tls: - hosts: - example.com secretName: example-com-tls rules: - host: example.com http: paths: - path: / pathType: Prefix backend: service: name: example-web port: number: 80
注意:
1 ingressClassName: your-ingress-class
必须替换成集群实际存在的 IngressClass:
1 kubectl get ingressclass
仅仅创建 Ingress Resource 并不会自动产生入口流量,集群必须已经存在对应的 Ingress Controller。
不同 Controller 的 HTTP -> HTTPS 强制跳转配置并不完全一样,很多时候依赖 Controller 自己的 Annotation,因此不能把某一家 Controller 的注解当成 Kubernetes Ingress 标准能力。
如果现有老集群仍使用已经退役的社区 ingress-nginx,常见配置曾经是:
1 2 3 4 metadata: annotations: nginx.ingress.kubernetes.io/ssl-redirect: "true" nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
这段只适用于相应 Controller,不应该复制到其他 Ingress Controller,更不建议为了使用这两个注解而在新集群中重新引入已退役的 ingress-nginx。
Kubernetes 自动签发和续期证书 生产集群不建议靠人工复制证书然后等到过期前再手工替换。
常见方案是使用 cert-manager 对接:
Let’s Encrypt / ACME。
企业内部 CA。
Vault PKI。
其他支持的证书签发系统。
例如由 Certificate 资源维护证书:
1 2 3 4 5 6 7 8 9 10 11 12 apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: example-com namespace: web-prod spec: secretName: example-com-tls dnsNames: - example.com issuerRef: name: letsencrypt-prod kind: ClusterIssuer
签发成功后,cert-manager 将证书和私钥写入:
Gateway 再引用这个 Secret:
1 2 3 4 5 tls: certificateRefs: - kind: Secret group: "" name: example-com-tls
这样 Gateway 与证书签发职责分离:
1 2 3 4 5 6 7 8 cert-manager | | create / renew v TLS Secret | v Gateway :443
ClusterIssuer 中 ACME Solver 的写法与 DNS、Gateway Controller、云平台和网络环境有关,应根据实际环境配置,不建议直接复制一份固定模板到所有集群。
Docker 与 Kubernetes 场景怎么选 Docker / Docker Compose 适合:
单机部署。
中小型内部系统。
开发/测试环境。
服务数量不多。
暂时不需要复杂弹性扩缩容。
典型结构:
1 2 3 4 Nginx Container | v Web Container
Nginx 同时负责:
HTTPS。
HTTP -> HTTPS。
域名。
反向代理。
静态文件。
Kubernetes 适合:
多节点。
多副本。
滚动升级。
自动扩缩容。
多服务统一入口。
更完整的云原生治理。
典型结构:
1 2 3 4 5 Gateway | Service | Deployment / Pods
Gateway/入口层负责:
HTTPS。
HTTP -> HTTPS。
域名。
路由。
应用 Pod 负责:
不要让每个业务 Pod 都重复保存同一份公网 TLS 私钥,除非明确需要端到端 TLS 或 mTLS。
动静分离 动静分离的核心思想是:静态资源尽量由 Nginx 直接返回,动态请求交给后端应用。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 server { listen 80 ; server_name example.com; location /static/ { alias /var/www/static/; expires 7d ; add_header Cache-Control "public" ; } location / { proxy_pass http://127.0.0.1:8080; 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 ; } }
这里使用:
请求:
对应:
1 /var/www/static/logo.png
root 和 alias 的路径拼接方式不同,配置静态目录时要特别注意。
反向代理 反向代理是客户端访问 Nginx,Nginx 再代表客户端访问真实后端服务。
客户端看到:
真正的后端可能是:
1 2 3 10.0.0.11:8080 10.0.0.12:8080 10.0.0.13:8080
基本配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 upstream backend { server 10.0.0.11:8080 ; server 10.0.0.12:8080 ; server 10.0.0.13:8080 ; }server { listen 80 ; server_name example.com; location / { proxy_pass http://backend; 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 ; } }
反向代理可以实现:
隐藏后端真实地址。
TLS 统一终止。
多实例负载均衡。
缓存。
超时控制。
限流。
域名和路径路由。
负载均衡 默认轮询 1 2 3 4 5 upstream backend { server backend1.example.com; server backend2.example.com; server backend3.example.com; }
权重 1 2 3 4 5 upstream backend { server backend1.example.com weight=3 ; server backend2.example.com weight=1 ; server backend3.example.com weight=2 ; }
最少连接 1 2 3 4 5 6 7 upstream backend { least_conn; server backend1.example.com; server backend2.example.com; server backend3.example.com; }
IP Hash 1 2 3 4 5 6 7 upstream backend { ip_hash; server backend1.example.com; server backend2.example.com; server backend3.example.com; }
ip_hash 可以提高同一个客户端落到相同后端的概率,但在现代应用中,更推荐让 Session 外置到 Redis、数据库或 Token 中,使 Web 实例本身无状态,而不是依赖负载均衡层维持会话粘滞。
基础失败检测 1 2 3 4 upstream backend { server backend1.example.com max_fails=3 fail_timeout=30s ; server backend2.example.com max_fails=3 fail_timeout=30s ; }
Nginx 缓存 Nginx 可以缓存反向代理响应:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 http { proxy_cache_path /var/cache/nginx \ levels=1 :2 \ keys_zone=my_cache:100m \ inactive=60m \ max_size=10g ; server { listen 80 ; server_name example.com; location /api/public/ { proxy_cache my_cache; proxy_cache_key "$scheme $request_method $host $request_uri " ; proxy_cache_valid 200 10m ; proxy_cache_valid 404 1m ; proxy_cache_use_stale error timeout updating; add_header X-Cache-Status $upstream_cache_status always; proxy_pass http://backend; } } }
缓存不要无脑加在所有 API 上,尤其是:
用户个人数据。
权限相关接口。
支付和订单接口。
实时库存。
响应依赖 Cookie/Authorization 的接口。
否则“性能优化”很可能瞬间升级成“数据串号体验”。
清除缓存 不建议暴露一个类似:
的公网接口然后执行 rm -rf。
更稳妥的做法是:
调整 cache key 和 cache version。
通过运维脚本清理缓存目录。
配置权限受控的缓存失效机制。
使用支持精细 Purge 的产品或模块。
简单运维场景可以在确认 Nginx 已停止访问缓存文件后,通过系统命令清理对应缓存目录,再重新加载服务。
HTTPS 混合内容问题 即使入口已经是 HTTPS,如果页面中仍然写死:
1 2 http://api.example.com http://cdn.example.com/logo.png
浏览器仍然可能报 Mixed Content,并拒绝加载部分资源。
因此切换 HTTPS 时要同时检查:
前端 API Base URL。
图片、CSS、JS、字体地址。
CDN 地址。
WebSocket 地址,ws:// 是否需要改成 wss://。
OAuth2/SSO Callback URL。
第三方回调。
Spring Boot 生成的绝对 URL。
不要只改 Nginx 的 443 配置,就认为整个系统已经完成 HTTPS 改造。
HTTPS 证书更新 证书不是“配置一次永久有效”。
生产环境至少应该监控:
证书过期时间。
自动续期是否成功。
私钥和证书是否匹配。
中间证书链是否完整。
新证书更新后 Nginx/Gateway 是否已经重新加载。
对于裸机 Nginx,更新证书后通常执行:
1 nginx -t && systemctl reload nginx
Docker:
1 2 docker compose exec nginx nginx -t && \ docker compose exec nginx nginx -s reload
Kubernetes 则更适合交给 cert-manager 或云平台证书系统自动维护 Secret 和入口配置。
防火墙开放 HTTP/HTTPS Rocky Linux/RHEL 使用 firewalld 时:
1 2 3 sudo firewall-cmd --permanent --add-service=httpsudo firewall-cmd --permanent --add-service=httpssudo firewall-cmd --reload
查看:
1 sudo firewall-cmd --list-all
云服务器还要同时检查云厂商安全组或防火墙规则。
常用排障命令 Nginx 检查配置:
打印全部生效配置:
查看进程:
查看监听端口:
查看日志:
1 2 tail -f /var/log/nginx/access.logtail -f /var/log/nginx/error.log
HTTP/HTTPS 1 2 curl -I http://example.com curl -I https://example.com
确认 HTTP 是否跳转 HTTPS:
1 curl -I http://example.com
预期类似:
1 2 HTTP/1.1 301 Moved Permanently Location: https://example.com/
或者:
1 2 HTTP/1.1 308 Permanent Redirect Location: https://example.com/
查看完整跳转链:
1 curl -IL http://example.com
TLS 1 2 3 openssl s_client \ -connect example.com:443 \ -servername example.com
重点检查:
Subject/SAN 是否包含域名。
Issuer 是否正确。
Verify return code 是否为 0。
证书链是否完整。
Docker 1 2 3 docker compose ps docker compose logs -f nginx docker compose exec nginx nginx -t
验证 Nginx 容器能否访问 Web:
1 docker compose exec nginx wget -S -O- http://web:8080/
Kubernetes 1 2 3 4 kubectl get pods -n web-prod kubectl get svc -n web-prod kubectl get gateway -n web-prod kubectl get httproute -n web-prod
查看状态:
1 2 kubectl describe gateway example-gateway -n web-prod kubectl describe httproute example-web -n web-prod
查看 Endpoint:
1 kubectl get endpointslices -n web-prod
如果 Service 没有 Endpoint,先排查 Pod Label 和 Service Selector,不要第一时间怀疑 Gateway。
常见错误总结 Nginx 配置成功但 443 访问不了 检查:
再检查:
Nginx 是否加载了新配置。
防火墙是否开放 443。
云安全组是否开放 443。
证书路径是否正确。
私钥权限是否允许 Nginx 读取。
HTTP 没有自动跳 HTTPS 确认存在单独的 80 端口 Server:
1 2 3 4 5 server { listen 80 ; server_name example.com; return 308 https://example.com$request_uri ; }
Docker Nginx 报 502 检查:
然后进入 Nginx 容器访问后端:
1 docker compose exec nginx wget -S -O- http://web:8080/
最常见原因:
写成 127.0.0.1:8080。
Service Name 写错。
Web 容器实际没启动。
Web 只监听 127.0.0.1 而不是容器的 0.0.0.0。
后端端口不是 8080。
Kubernetes Gateway 有地址但访问 503 优先检查:
1 kubectl get svc,pod,endpointslices -n web-prod
如果 Service 后面没有健康 Endpoint,Gateway 再正常也无法把请求交给应用。
浏览器提示证书不安全 常见原因:
使用自签名证书。
域名和 SAN 不匹配。
证书过期。
中间证书缺失。
客户端不信任内部 CA。
一份完整的单机生产配置示例 假设:
1 2 3 4 域名:example.com 后端:127.0.0.1:8080 证书:/etc/nginx/ssl/fullchain.pem 私钥:/etc/nginx/ssl/privkey.pem
完整示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 upstream web_backend { least_conn; server 127.0.0.1:8080 max_fails=3 fail_timeout=30s ; keepalive 32 ; }server { listen 80 ; listen [::]:80 ; server_name example.com www.example.com; return 308 https://example.com$request_uri ; }server { listen 443 ssl; listen [::]:443 ssl; server_name example.com; ssl_certificate /etc/nginx/ssl/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3 ; ssl_session_cache shared:SSL:10m ; ssl_session_timeout 10m ; ssl_session_tickets off ; server_tokens off ; client_max_body_size 20M ; access_log /var/log/nginx/example.access.log main; error_log /var/log/nginx/example.error .log warn ; location / { proxy_pass http://web_backend; proxy_http_version 1 .1 ; 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 ; proxy_set_header X-Forwarded-Host $host ; proxy_set_header X-Forwarded-Port $server_port ; proxy_set_header Connection "" ; proxy_connect_timeout 5s ; proxy_send_timeout 60s ; proxy_read_timeout 60s ; } }
如果确认全站已经稳定支持 HTTPS,再根据实际情况增加 HSTS。
最终部署建议 如果是单机 Docker:
1 2 3 4 5 6 7 Internet | Nginx Container :80/:443 | Docker Network | Web Container :8080
建议:
只有 Nginx 暴露公网端口。
Nginx 负责 HTTPS 和 HTTP -> HTTPS。
证书通过只读 volume 挂载。
Web 通过 Compose Service Name 访问。
修改配置先 nginx -t。
如果是 Kubernetes:
1 2 3 4 5 6 7 Internet | Gateway | Service | Web Pods
建议:
新项目优先 Gateway API。
TLS 在 Gateway 终止。
HTTPRoute 负责 HTTP -> HTTPS。
Web Service 使用 ClusterIP。
Pod 不直接暴露公网。
证书交给 Secret + cert-manager/外部证书系统管理。
已有 Ingress 可以继续使用,但应确认 Controller 的维护状态,不要把已经退役的 ingress-nginx 作为新集群默认选择。
参考资料
Nginx 官方文档:Module ngx_http_ssl_module
Nginx 官方文档:Configuring HTTPS servers
Nginx 官方文档:Reverse Proxy
Docker 官方文档:Compose networking
Kubernetes 官方文档:Ingress
Kubernetes 官方文档:Gateway API
Gateway API 官方文档:HTTP redirects and rewrites
Gateway API 官方文档:TLS Configuration
Kubernetes Blog:Ingress NGINX Retirement
cert-manager 官方文档
《Nginx配置免费HTTPS详细教程》:https://blog.csdn.net/XiaoXiaoYunXing/article/details/134440485
启示录 富贵岂由人,时会高志须酬。
能成功于千载者,必以近察远。