Skip to content

Nginx ​

Nginx介绍 ​

Nginx是一款轻量级、高性能的 HTTP 和反向代理服务器,

Nginx 采用事件驱动的异步非阻塞模型,能够高效地处理大量并发连接。与传统的 Web 服务器(如 Apache)相比,Nginx 在性能表现上具有显著优势,尤其在高并发场景下,Nginx 能够保持较低的资源消耗,提供更快的响应速度

Nginx安装 ​

安装环境准备 ​

本文以 CentOS 系统为例进行安装演示。在安装 Nginx 之前,需要确保系统已经安装了必要的依赖包,Nginx 基于 C 语言开发,因此需要安装 C 语言编译环境以及其他相关依赖。执行以下命令安装依赖包:

bash
yum -y install gcc pcre-devel zlib-devel openssl openssl-devel

其中, gcc 是 C 语言编译器, pcre-devel 用于支持 PCRE 正则表达式库(Nginx 配置中常用到正则表达式), zlib-devel 用于支持数据压缩, openssl 和 openssl-devel 用于支持 SSL/TLS 加密功能。

下载 Nginx 安装包 ​

Nginx 官方网站提供了稳定版本和开发版本的下载。通常建议在生产环境中使用稳定版本,以确保系统的稳定性和安全性。访问 Nginx 官网的下载页面( nginx: download ),选择合适的稳定版本,本文以 nginx-1.24.0 为例,使用 wget 命令下载安装包:

bash
yum install wget
wget https://nginx.org/download/nginx-1.24.0.tar.gz

解压与配置编译环境 ​

下载完成后,解压 Nginx 压缩包,并进入解压后的目录进行编译环境配置:

bash
tar -zxvf nginx-1.24.0.tar.gz
cd nginx-1.24.0
./configure --prefix=/usr/local/nginx

--prefix=/usr/local/nginx 参数指定了 Nginx 的安装目录为 /usr/local/nginx ,你可以根据实际需求进行调整。 configure 脚本会检查系统环境,确保满足 Nginx 编译所需的依赖条件,并生成相应的 Makefile 文件。

编译与安装 ​

执行以下命令进行编译和安装:

bash
make & make install

make 命令根据之前生成的 Makefile 文件编译 Nginx 源代码, make install 则将编译好的 Nginx 二进制文件及相关配置文件安装到指定的目录(即 /usr/local/nginx )。

启动与基本管理 ​

安装完成后,进入 Nginx 的安装目录 /usr/local/nginx/sbin ,可以使用以下命令对 Nginx 进行管理:

bash
# 查看版本
./nginx -v
# 检查配置文件语法
./nginx -t
# 启动Nginx
./nginx
# 停止Nginx
./nginx -s stop
# 重新加载配置文件(修改配置后使用)
./nginx -s reload

为了方便使用,建议将 Nginx 的 sbin 目录添加到系统的环境变量中。编辑 /etc/profile 文件,在 PATH 环境变量中增加 /usr/local/nginx/sbin ,修改完成后执行 source /etc/profile 使配置生效。这样,在系统的任何目录下都可以直接执行 nginx 命令。


基础配置逻辑 ​

Nginx 存放静态资源的核心是:通过 server 块区分不同访问入口(域名或端口),再用 location 块指定资源存放路径,让不同请求对应到正确的本地文件。

按域名 / 端口区分不同业务 ​

text
# 用域名区分(需解析域名)
server {
listen 80;
server_name member.hltop.cn;  # 会员业务域名
location / {
root html/member;  # 资源存放在 Nginx安装目录/html/member
index index.html;  # 访问根路径时默认找index.html
}
}
server {
listen 80;                  # 同一80端口,不同域名
server_name order.hltop.cn;   # 订单域名
location / {
root /var/www/order;
index index.html;
}
}
-------------------------------------------------------
# 用端口区分(适合本地测试)
server {
listen 8081;  # 用8081端口访问会员业务
location / {
root html/member;
index index.html;
}
server {
listen 8082;                # 测试端口2
location / {
root /var/www/test2;
index index.html;
}
}

root 指令:路径拼接方式 ​

root 是最常用的指定资源目录的指令,它的路径拼接规则很简单:
最终文件路径 = root 指定的目录 + location 匹配的路径 + 请求的文件名

text
location /img/ {
root /var/www/static;  # 根目录设为/var/www/static
}

当客户端请求 http://domain.com/img/logo.png 时:

  1. location 匹配到 /img/
  2. 拼接路径: /var/www/static (root) + /img/ (location 匹配的路径) + logo.png (请求的文件名)
  3. 实际查找的文件: /var/www/static/img/logo.png

alias 指令:路径替换方式 ​

alias 用于 替换 location 匹配的路径,适合需要重命名目录的场景。它的拼接规则是:
最终文件路径 = alias 指定的目录 + 请求中 location 之后的部分
(注意: alias 后面的目录路径必须以 / 结尾)

text
location /img/ {
alias /var/www/static/image/;  # 用/image/替换/img/
}

当客户端请求 http://domain.com/img/logo.png 时:

  1. location 匹配到 /img/ ,但这个路径会被 alias 替换
  2. 拼接路径: /var/www/static/image/ (alias) + logo.png (请求中 /img/ 之后的部分)
  3. 实际查找的文件: /var/www/static/image/logo.png

root 和 alias 的核心区别

场景root 指令alias 指令
作用设定根目录,路径叠加替换 location 匹配的路径
路径要求无特殊要求目录必须以 / 结尾
适用场景大多数静态资源映射需重命名目录的场景(如 URL 显示 /img/,实际用 /image/ 目录)

简单说: root 是「在指定目录下追加路径」, alias 是「直接替换路径」。根据实际目录结构选择即可。

HTTPS 配置 ​

发布静态资源后,如果需要开启 HTTPS 协议,需要配置 Nginx 的 SSL 证书。

配置参考:

text
server {
	listen 443 ssl;
	server_name localhost;

	ssl_certificate     ssl/server.crt;
	ssl_certificate_key ssl/server.key;

	location / {
		root   html/dist;
		index  index.html index.htm;
		try_files $uri $uri/ /index.html;
	}
}

上述示例中证书server.crt和私钥server.keys使用的是相对位置,所在的ssl目录实际应该位于Nginx安装目录的conf目录下(在windows平台版本1.30.4的环境下,实测ssl目录要放在conf目录下才能被正确找到,放在安装目录启动会有失败日志cannot load certificate "D:\PRODUCTION\nginx_8888_imis/conf/ssl/server.crt": BIO_new_file() failed (SSL: error:80000003:system library::No such process:calling fopen(D:\PRODUCTION\nginx_8888_imis/conf/ssl/server.crt, r) error:10000080:BIO routines::no such file),网上教程大部分是放在安装目录,不知是否是该版本的缺陷还是新旧版本差异)。当然,我们可以直接使用绝对路径。

正向代理与反向代理 ​

正向代理 ​

概念 正向代理是位于客户端和目标服务器之间的代理服务器。当客户端需要访问外部资源时,客户端将请求发送到正向代理服务器,代理服务器代替客户端向目标服务器发送请求,并将目标服务器的响应返回给客户端。

正向代理是指代理服务器代理 客户端 ,使得客户端可以访问被代理的服务器上的资源。举个例子,我们可以使用正向代理访问一些境外网站,这些网站被代理服务器所代理,客户端看到的是代理服务器响应的内容。这时,被代理的服务器并不知道客户端的存在,只知道代理服务器的存在。正向代理主要应用于隐藏客户端的 IP 地址、访问互联网被封锁的资源等场景

Nginx 实现正向代理配置 在 Nginx 中配置正向代理,需要编辑 Nginx 的配置文件,通常位于 /usr/local/nginx/conf/nginx.conf 。在 http 块内添加如下配置:

text
server {
listen 8080; # 代理服务器监听端口
resolver 8.8.8.8; # 指定DNS服务器,用于域名解析
location / {
proxy_pass http://$http_host$request_uri;
}
}

上述配置中, listen 指令指定了代理服务器监听的端口为 8080 ,客户端将请求发送到该端口。 resolver 指令指定了 DNS 服务器地址(这里使用 Google 的公共 DNS 服务器 8.8.8.8 ),用于将域名解析为 IP 地址。 location / 块表示对所有请求进行处理, proxy_pass 指令将客户端的请求转发到目标服务器, $http_host 和 $request_uri 是 Nginx 的内置变量,分别表示客户端请求的主机名和请求的 URI。

配置完成后,检查配置文件语法并重新加载 Nginx 配置:

bash
nginx -t
nginx -s reload

此时, 客户端需要将代理服务器的地址和端口配置到网络设置 中,才能通过 Nginx 正向代理访问外部资源。需要注意的是,Nginx 的正向代理默认不支持 HTTPS 站点,如果需要代理 HTTPS 请求,还需要进行额外的配置。

反向代理 ​

概念 反向代理位于客户端与目标服务器之间。客户端向反向代理服务器发送请求,反向代理会依据配置规则,把请求转发到后端的目标服务器,再将目标服务器的响应回传给客户端。对客户端而言,它不清楚具体是后端哪台服务器处理了请求,仅与反向代理交互,反向代理隐藏了后端服务器的真实 IP 和架构细节。

反向代理常用于为服务器端提供服务,可实现负载均衡(比如按算法选合适服务器转发请求)、缓存加速、安全防护(隐藏后端信息、抵御直接攻击),还能进行 SSL 加密等,主要在数据安全性、访问速度、系统弹性等方面发挥作用。

Nginx 实现反向代理配置 在 Nginx 中配置反向代理同样需要编辑配置文件。假设后端有一台 Web 服务器,IP 地址为 192.168.1.100 ,端口为 8080 ,现在要通过 Nginx 反向代理将客户端对 http://example.com 的请求转发到该后端服务器。在 nginx.conf 文件的 http 块内添加如下 server 块配置:

text
server {
listen 80; # 监听端口
server_name example.com; # 域名或IP地址
location / {
proxy_pass http://192.168.1.100:8080; # 反向代理到后端服务器
proxy_set_header Host $host; # 设置请求头,将客户端请求的主机名传递给后端服务器
proxy_set_header X-Real-IP $remote_addr; # 设置请求头,将客户端的真实IP传递给后端服务器
}
}

listen 指令指定 Nginx 监听的端口为 80 , server_name 指定了反向代理的域名或 IP 地址(这里假设为 example.com ,实际使用时需替换为真实的域名或 IP)。 location / 块表示对所有请求进行匹配处理, proxy_pass 指令将请求转发到后端服务器 http://192.168.1.100:8080 。 proxy_set_header 指令用于设置请求头信息,将客户端的一些信息(如主机名、真实 IP)传递给后端服务器,这在后端服务器需要根据这些信息进行处理时非常重要。

配置完成后,同样需要检查配置文件语法并重新加载 Nginx 配置,使配置生效。

这段配置的作用就是:当用户访问 example.com (通过 80 端口)时,Nginx 会自动将请求 悄悄转发 到后端的 http://192.168.1.100:8080 服务器,而用户完全感知不到这个转发过程 —— 在用户看来,自己就是直接访问 example.com 并得到了响应。

正向代理和反向代理区别 ​

区别:

  • 正向代理 :客户端明确知道自己在通过代理服务器访问目标服务器(比如设置浏览器代理),整个过程是 “客户端→代理→目标服务器”,客户端主动依赖代理才能完成请求。
  • 反向代理 :客户端以为自己直接访问的是 “目标服务器”(实际是反向代理服务器的地址),完全不知道后端有多少台真实服务器,也不关心代理如何转发请求。对客户端来说,“反向代理服务器” 就是它眼中的 “目标服务器”,整个过程是 “客户端→反向代理(以为是目标服务器)→后端真实服务器”,代理行为对客户端完全透明。

在简单说:

  • 正向代理是 “ 客户端主动找代理帮忙上网 ”;
  • 反向代理是 “ 后端服务器偷偷用代理对外提供服务 ”,客户端被蒙在鼓里。

负载均衡算法 ​

Nginx 支持多种负载均衡算法,每种算法都有其特点和适用场景:

1.轮询(Round Robin) :默认算法,每个请求按时间顺序逐一分配到不同的后端服务器。如果后端服务器出现故障,Nginx 会自动将其剔除,请求将分配到其他正常的服务器上。例如:

text
upstream backend_servers {
server 192.168.1.101:8080;
server 192.168.1.102:8080;
}

2.权重(Weighted Round Robin) :为后端服务器设置不同的权重,权重越高的服务器被分配到的请求越多。适用于后端服务器性能不均的情况,例如:

text
upstream backend_servers {
server 192.168.1.101:8080 weight=3;
server 192.168.1.102:8080 weight=1;
}

这里 192.168.1.101 服务器的权重为 3, 192.168.1.102 服务器的权重为 1,那么在负载均衡时, 192.168.1.101 服务器被分配到请求的概率将是 192.168.1.102 服务器的 3 倍。
3. IP 哈希(IP Hash) :根据客户端的 IP 地址计算哈希值,将请求分配到固定的后端服务器上。这样可以确保同一客户端的请求始终被转发到同一台后端服务器,适用于需要保持会话一致性的场景,例如:

text
upstream backend_servers {
ip_hash;
server 192.168.1.101:8080;
server 192.168.1.102:8080;
}

四层代理 ​

说到nginx代理,一般都指七层代理。nginx也支持四层代理。

配置示例:

text
stream {
    upstream mysql_backend {
        server 10.0.0.1:3306;
        server 10.0.0.2:3306;
    }

    server {
        listen 3306;
        proxy_pass mysql_backend;
    }
}

什么情况下使用四层代理? 非 HTTP 协议的代理 — 这是最核心的场景。MySQL、Redis、PostgreSQL、MongoDB、RabbitMQ、DNS 等都是 TCP/UDP 协议,七层代理根本无法处理,只能用四层。

追求极致性能 — 四层不解析应用层报文,转发延迟更低,吞吐更高。对于高并发短连接场景(如游戏服务器、物联网设备接入),四层代理的性能优势明显。

TLS 透传(SNI 路由) — 不想在负载均衡层解密 TLS(避免证书管理复杂度或保持端到端加密),四层可以根据 TLS 握手的 SNI 字段做路由,而不需要解密流量:

七层代理提供了路径路由、Header 操作、缓存、限流、灰度发布等丰富能力,这些是四层做不到的。只有当协议不是 HTTP、或者对性能/TLS 透传有硬性要求时,才选择四层。实际生产中,两者经常组合使用:四层在最前端做流量入口和 TLS 卸载/透传,七层在后面做精细化路由。

跨域问题(CORS) ​

跨域问题详解 ​

1. 什么是跨域?

跨域是指浏览器出于安全考虑( 同源策略 ),限制一个域的网页去请求另一个域的资源。

  • 同源 :指两个 URL 的 协议、域名、端口 完全一致。例如:
    • http://example.com 与 https://example.com (协议不同,跨域)
      • http://example.com 与 http://api.example.com (域名不同,跨域)
      • http://example.com:80 与 http://example.com:8080 (端口不同,跨域)

2. 跨域的常见场景

  • 前端页面( http://frontend.com )请求后端 API( http://backend.com )
  • 静态资源(如图片、JS)从 http://a.com 加载到 http://b.com 的页面中
  • 前后端分离项目中,前端本地开发环境( http://localhost:3000 )请求后端服务器( http://localhost:8080 )

3. 跨域的本质:浏览器的同源策略限制

同源策略是浏览器的安全机制,防止恶意网站窃取其他网站的资源或数据。当跨域请求发生时,浏览器会拦截响应(即使服务器正常返回数据),并在控制台报类似错误:

text
Access to fetch at 'http://backend.com/api' from origin 'http://frontend.com' has been blocked by CORS policy...

Nginx 解决跨域的原理与配置 ​

Nginx 解决跨域的核心思路是: 通过反向代理将跨域请求转为同域请求 。
即前端页面请求 Nginx 服务器(同域),Nginx 再转发请求到真实后端服务器,从而绕过浏览器的同源策略限制。

1. 场景假设

  • 前端页面地址: http://frontend.com (端口 80)
  • 后端 API 地址: http://backend.com:8080 (跨域,协议 / 域名 / 端口任一不同)
  • 目标:让前端通过 http://frontend.com/api 访问后端 API,实现 “同域” 请求。

2. Nginx 配置示例

text
server {
listen 80;
server_name frontend.com;  # 前端页面的域名(同域)
# 处理前端页面的静态资源(如 HTML、CSS、JS)
location / {
root /path/to/frontend;  # 前端代码存放目录
index index.html;
}
# 反向代理后端 API,解决跨域
location /api/ {  # 前端请求以 /api 开头的路径时,触发代理
proxy_pass http://backend.com:8080/;  # 转发到真实后端(注意末尾的 / 含义)
# 关键:设置跨域相关响应头,允许浏览器接收
add_header Access-Control-Allow-Origin $http_origin;  # 允许请求的源($http_origin 为动态值,或者为*允许所有的跨域调用)
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;  # 允许的请求方法
add_header Access-Control-Allow-Headers Content-Type,Authorization;  # 允许的请求头
add_header Access-Control-Allow-Credentials true;  # 允许携带 Cookie(需前后端配合)
}
}

3. 配置说明

  • **proxy_pass** 转发规则 :
    当前端请求 http://frontend.com/api/user 时,Nginx 会转发到 http://backend.com:8080/user ( /api/ 被替换为后端的根路径)。
  • 跨域响应头作用 :
    • Access-Control-Allow-Origin :告诉浏览器 “允许 frontend.com 这个源的请求”, $http_origin 表示动态匹配请求的源(更灵活)
      • Access-Control-Allow-Credentials :如果前端请求需要携带 Cookie 或认证信息,必须设置为 true ,且 Allow-Origin 不能为 * (需指定具体域名)。

4. 效果

前端只需请求同域的 http://frontend.com/api/xxx ,Nginx 自动转发到后端,浏览器认为是同域请求,不再拦截,跨域问题解决。


动静态资源分离 ​

什么是动静态资源分离? ​

动静态分离是指将网站的 动态资源 和 静态资源 分开部署、管理和访问的架构设计。

  • 静态资源 :即所说的前端资源,指内容不随请求变化的文件,如 HTML、CSS、JS、图片(jpg/png)、视频、字体等(可被浏览器缓存)。
  • 动态资源 :即后端请求,指内容随请求参数或业务逻辑动态生成的资源,如 PHP/JSP/Java 代码生成的页面、API 接口返回的 JSON 数据等(需服务器计算后返回)。

通过分离,可让静态资源由专门的服务器(如 Nginx)处理,动态资源由应用服务器(如 Tomcat、Node.js)处理,从而提升整体性能。

为什么需要动静态分离? ​

  1. 减轻应用服务器压力 :
    静态资源访问频率高(如首页图片、样式表),若全部由处理动态逻辑的应用服务器承担,会占用其 CPU / 内存资源,影响动态请求的响应速度。
  2. 提升静态资源加载速度 :
    Nginx 处理静态资源的效率远高于应用服务器(底层基于异步非阻塞 IO,适合高并发静态文件读取),且可配合浏览器缓存、CDN 进一步加速。
  3. 便于缓存和扩展 :
    静态资源可设置长期缓存策略,或通过 CDN 分发到边缘节点;动态资源则可专注于业务逻辑,按需扩容应用服务器。

Nginx 实现动静态分离的核心思路 ​

Nginx 通过 **location** 指令匹配不同资源路径 ,将静态资源请求直接由 Nginx 本地处理(读取服务器文件),动态资源请求转发到后端应用服务器(如 Tomcat、Spring Boot 等)。

假设场景:

  • 静态资源(CSS/JS/ 图片)存放路径: /usr/share/nginx/static
  • 动态资源由后端应用服务器 http://192.168.1.101:8080 处理

配置如下:

text
server {
listen 80;
server_name example.com;  # 网站域名
# 1. 处理静态资源:匹配以 /static/ 开头的路径(如 CSS、JS、图片)
location /static/ {
root /usr/share/nginx;  # 静态资源根目录(实际路径为 /usr/share/nginx/static/)
try_files $uri =404;  # 若文件不存在,返回404
}
# 2. 处理动态资源:所有非静态资源的请求转发到后端应用服务器
location / {
proxy_pass http://192.168.1.101:8080;  # 转发到动态服务器
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}

常见问题及解决方法 ​

访问单页面应用404问题 ​

单页面应用部署时,常遇到刷新页面后,路由跳转失败的问题。例如:vue工程打包输出dist目录,我们使用nginx部署,常配置如下:

text
location / {
    root   html/dist;
    index  index.html index.htm;
}

首次访问如:http://localhost:8080,页面正常跳转http://127.0.0.1:8080/mine/flight-routes, 再刷新页面,出现404错误。 这是 Vue SPA 部署到 nginx 的经典问题。因为 Vue Router 是在客户端处理路由,而 nginx 是在服务器端处理路由。原因:刷新浏览器时,nginx 收到 /mine/flight-routes 的 GET 请求,去 html/dist/ 目录下找 mine/flight-routes 文件,找不到就返回 404。Vue Router 的前端路由对 nginx 来说是不可见的。

在 nginx 配置的 location / 块里加一行 try_files 即可:

text
location / {
    root   html/dist;
    index  index.html index.htm;
    try_files $uri $uri/ /index.html;
}

这样 nginx 找不到对应文件时,会把请求交给 index.html,由 Vue Router 在客户端解析路由,页面就能正常渲染了。改完后 nginx -s reload 重新加载配置即可。

try_files 是 nginx 的一个指令,它按顺序依次检查多个文件路径,第一个存在的就直接返回,全部不存在则把请求交给最后一个参数处理。

try_files $uri $uri/ /index.html; 展开来就是三步查找:

$uri — 把请求的路径当作文件去找。比如访问 /assets/index.2568ccb9.js,nginx 就去 root 目录下找 assets/index.2568ccb9.js 这个文件。找到了就直接返回,这是正常的静态资源命中路径。

$uri/ — 把请求路径当作目录去找。比如访问 /foo,nginx 去找 foo/ 目录,如果存在就尝试返回目录下的 index.html。这个在实际 SPA 项目中用得少,主要是为了兼容某些路径末尾不带斜杠的目录访问场景。

/index.html — 前面两步都没命中,就把 /index.html 作为内部重写目标返回给浏览器。注意这不是 302 重定向,是内部 rewrite,浏览器地址栏不会变。浏览器拿到 index.html 后,Vue Router 接管,根据 URL 中的 /mine/flight-routes 渲染对应组件。

整个流程用你的场景举例:

用户首次访问 http://127.0.0.1:8080 → nginx 返回 index.html → Vue 启动 → Router 跳转到 /mine/flight-routes(纯客户端行为,不发 HTTP 请求)。

用户刷新页面 → 浏览器发 GET /mine/flight-routes → nginx 查找 html/dist/mine/flight-routes 文件(不存在)→ 查找 html/dist/mine/flight-routes/ 目录(不存在)→ 最终返回 html/dist/index.html → Vue 启动 → Router 解析 URL 并渲染页面。

没有 try_files 的情况下,nginx 找不到文件就直接 404 了,根本不会尝试返回 index.html。