Skip to content

前端必备 Nginx 知识点

Nginx在应用程序中的作用

Nginx(发音为“engine x”)是一款轻量级、高性能的HTTP服务器和反向代理服务器。对于前端开发者而言,它不仅仅是部署应用的工具,更是解决开发和生产环境中诸多问题的利器。其主要作用包括:

  • 解决跨域:通过反向代理,将不同源的请求转发到同一域名下。
  • 请求过滤:根据状态码、URL、请求方法等条件进行访问控制或重定向。
  • 配置gzip:开启压缩,显著减小传输文件体积,提升加载速度。
  • 负载均衡:将流量分发到多个应用服务器,提高系统的可用性和性能。
  • 静态资源服务器:高效地托管图片、JS、CSS等文件,并可设置强缓存。

Nginx是一个高性能的HTTP和反向代理服务器,也是一个通用的TCP/UDP代理服务器,最初由俄罗斯人Igor Sysoev编写。

正向代理与反向代理

代理是在服务器和客户端之间架设的一层服务器。代理接收客户端的请求并将其转发给目标服务器,然后将服务器的响应转发回客户端。它扮演了“中介”的角色。

不管是正向代理还是反向代理,实现的都是上面的功能。

代理

正向代理

正向代理,是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。

正向代理是为客户端服务的,客户端可以根据正向代理访问到它本身无法访问到的服务器资源。

正向代理对客户端是透明的,对服务端是非透明的,即服务端并不知道自己收到的是来自代理的访问还是来自真实客户端的访问。

反向代理

反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。

反向代理是为服务端服务的,反向代理可以帮助服务器接收来自客户端的请求,帮助服务器做请求转发,负载均衡等。

反向代理对服务端是透明的,对客户端是非透明的,即我们并不知道自己访问的是代理服务器,而服务器知道反向代理在为他服务。

基本配置

nginx.conf 配置

Nginx配置文件采用分层的块状结构。

nginx
# 全局块,设置影响nginx全局的指令
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Events块,配置影响nginx服务器与用户的网络连接
events {
    worker_connections 1024;
    use epoll;
}

# Http块,可以嵌套多个Server,是配置的主要部分
http {
    include /etc/nginx/mime.types; # 文件扩展名与MIME类型映射
    default_type application/octet-stream; # 默认MIME类型

    # 日志格式定义
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

    access_log /var/log/nginx/access.log main;

    # 可以包含其他配置文件,如`/etc/nginx/conf.d/*.conf`
    include /etc/nginx/conf.d/*.conf;

    # Server块,定义一个虚拟主机
    server {
        listen 80; # 监听端口
        server_name example.com www.example.com; # 域名

        # Location块,用于匹配URI
        location / {
            root /usr/share/nginx/html; # 根目录
            index index.html index.htm; # 默认文件
        }

        location /api/ {
            # 反向代理到后端应用
            proxy_pass http://backend_server;
        }
    }
}
  • main(全局块):影响nginx整体运行的配置指令,如用户、进程数、日志路径。
  • events:影响nginx服务器与用户的网络连接,如工作模式、单个进程最大连接数。
  • http:可以嵌套多个server,配置代理、缓存、日志、第三方模块等绝大多数功能。
  • server:配置虚拟主机的相关参数,一个http中可以有多个server。
  • location:用于匹配请求的URI,并定义如何处理这些请求。
  • upstream:定义后端服务器组,用于负载均衡。

内置变量

变量名功能
$host请求头中的Host字段,如果请求中没有该行,则等于server_name
$request_method客户端请求类型,如 GET、 POST
$remote_addr客户端的 IP 地址
$args请求中的参数
$content_length请求头中的 Content-length 字段
$http_user_agent客户端 agent 信息
$http_cookie客户端 cookie 信息
$remote_port客户端的端口
$server_protocol请求使用的协议,如 HTTP/1.0、 HTTP/1.1
$server_addr服务器地址
$server_name服务器名称
$server_port服务器的端口号
$request_uri原始请求的URI(带参数)
$uri当前请求的URI(不带参数,可能已被内部重写)
$scheme请求的协议,如 http 或 https

解决跨域

什么是跨域?

浏览器出于安全考虑,实施了同源策略 (Same-Origin Policy)。它限制了从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。 当一个请求的协议、域名、端口三者有任意一个与当前页面地址不同,即视为跨域。

同源策略限制的操作:

  • AJAX/Fetch请求(主要限制)
  • DOM访问,无法通过JavaScript访问不同源的iframe内容
  • Cookie、LocalStorage、SessionStorage、IndexedDB等存储访问
  • Web Workers、通常Worker脚本必须与主页面同源
  • WebGL纹理
  • Canvas图像操作(drawImage、getImageData等)
  • Web字体(CSS中@font-face)

Nginx 解决跨域的原理

  • 前端页面域名:https://fe.server.com
  • 后端服务域名:https://admin.server.com

如果在浏览器从 https://fe.server.com 直接向 https://admin.server.com 发起请求会出现跨域。

可以通过nginx配置解决跨域:

nginx
server {
  listen 80;
  server_name fe.server.com;
  location / {
    proxy_pass admin.server.com;
  }
}

请求过滤

根据状态码过滤

nginx
# 当发生 50x 系列错误时,返回 /50x.html 页面
error_page 500 502 503 504 /50x.html;
# 指定 404 错误页面
error_page 404 /404.html;

location = /50x.html {
    # root 路径相对于 nginx 配置的 prefix(默认为 /usr/share/nginx/html)
    root /usr/share/nginx/html;
    # 也可以使用绝对路径
    # root /path/to/your/error/pages;
    # 可以添加内部头,防止被当作静态文件直接访问
    internal;
}

根据URL过滤

nginx
location /admin {
    # 返回403禁止访问
    return 403;
    # 或者重定向到登录页
    # return 302 /login;
}

location /old-page {
    # 301 永久重定向,有利于SEO
    return 301 https://$host/new-page;
}

location = / {
    # rewrite 指令:`last`(内部重写,重新匹配location), `break`(停止重写,在当前location处理), `redirect`(302临时重定向), `permanent`(301永久重定向)
    rewrite ^ /home redirect; # 302 临时重定向到 /home
    # return 301 /home; # 301永久重定向到 /home
    # rewrite ^ /home last; # 内部重写,URL不变,但内容来自 /home 的处理逻辑
}

根据请求类型过滤

nginx
location /api {
    # 只允许 GET, POST, HEAD 方法,其他返回 403
    if ($request_method !~ ^(GET|POST|HEAD)$ ) {
        return 405; # Method Not Allowed 更标准
    }
    proxy_pass http://backend;
}

配置gzip

GZIP是规定的三种标准HTTP压缩格式之一。目前绝大多数的网站都在使用 GZIP传输 HTML、 CSS、 JavaScript 等资源文件。

对于文本文件, GZip 的效果非常明显,开启后传输所需流量大约会降至 1/4~1/3。

并不是每个浏览器都支持 gzip的,如何知道客户端是否支持 gzip呢,请求头中的 Accept-Encoding来标识对压缩的支持。

启用 gzip同时需要客户端和服务端的支持,如果客户端支持 gzip的解析,那么只要服务端能够返回 gzip的文件就可以启用 gzip了,我们可以通过 nginx的配置来让服务端支持 gzip。下面的 respone中 content-encoding:gzip,指服务端开启了 gzip的压缩方式。

nginx
http {
    # 开启gzip压缩功能
    gzip on;
    # 设置允许压缩的页面最小字节数,小于此值的文件可能不会压缩
    gzip_min_length 1k;
    # 配置压缩缓冲区,4个32k内存块
    gzip_buffers 4 16k;
    # 压缩级别,1-9,级别越高压缩率越高,但消耗CPU也越多。建议折中取 4-6
    gzip_comp_level 5;
    # 设置需要压缩的MIME类型,文本类型通常都值得压缩
    gzip_types text/plain text/css text/xml text/javascript application/javascript application/x-javascript application/json application/xml application/rss+xml image/svg+xml;
    # 根据请求头中的`User-Agent`和`Accept-Encoding`,对不支持gzip的古老客户端(如IE6)禁用压缩
    gzip_disable "MSIE [1-6]\.";
    # 为代理请求启用压缩(如果请求头已包含 Via)
    gzip_proxied any;
    # 告诉代理服务器在响应头中添加 `Vary: Accept-Encoding`,便于缓存区分
    gzip_vary on;
}

验证: 配置完成后,重启Nginx。在浏览器开发者工具的“Network”面板中,查看响应头,如果出现 Content-Encoding: gzip,则说明配置成功。

负载均衡

什么是负载均衡?

负载均衡(Load Balancing)就是将大量的客户端请求均匀地分发到多个后端服务器上,以避免单个服务器过载,从而提高应用程序的整体可用性、扩展性和性能。

Nginx如何实现负载均衡

使用 upstream 模块定义一组后端服务器,然后在 location 中使用 proxy_pass 指向这个 upstream 组。

nginx
http {
    # 定义名为 `backend_servers` 的后端服务器组
    upstream backend_servers {
        # 负载均衡策略在这里定义
        # 默认是轮询 (round-robin)
        server 192.168.1.100:8080 weight=3; # weight 权重,默认为1
        server 192.168.1.101:8080;
        server 192.168.1.102:8080 backup; # backup 标记为备份服务器,只有当主服务器都不可用时才启用
    }

    server {
        listen 80;
        server_name loadbalance.example.com;

        location / {
            # 将请求代理到 upstream 组
            proxy_pass http://backend_servers;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

负载均衡策略

  1. 轮询 (Round Robin):默认策略。按顺序将请求逐一分配到不同的后端服务器。

  2. 权重轮询 (Weighted Round Robin):在server后使用weight参数指定权重。权重越高,被分配到的请求比例越大。适用于服务器性能不均的场景。

nginx
upstream backend {
    server backend1.example.com weight=5;
    server backend2.example.com weight=1;
}
  1. IP哈希 (ip_hash):根据客户端IP地址计算哈希值,将同一IP的请求固定分配给同一个后端服务器。这能解决会话(Session)保持的问题。
nginx
upstream backend {
    ip_hash;
    server backend1.example.com;
    server backend2.example.com;
}
  1. 最少连接数 (least_conn):将新请求分配给当前活动连接数最少的服务器。适用于请求处理时间长短不一造成会话连接积压的情况。
nginx
upstream backend {
    least_conn;
    server backend1.example.com;
    server backend2.example.com;
}
  1. 基于响应时间的负载均衡(需要商业版Nginx Plus):将请求分配给平均响应时间最短的服务器。

健康检查

Nginx开源版默认的被动健康检查:当Nginx向后端服务器转发请求失败时(如连接超时、返回5xx错误),它会将该服务器标记为“不可用”,并在接下来的一段时间内暂时停止向其转发请求。

nginx
upstream backend {
    server backend1.example.com max_fails=3 fail_timeout=30s;
    server backend2.example.com max_fails=3 fail_timeout=30s;
}
  • max_fails=3:允许请求失败的最大次数。
  • fail_timeout=30s:在30秒内,如果失败次数达到max_fails,则将该服务器标记为不可用30秒。

主动健康检查需要Nginx Plus或第三方模块(如 nginx_upstream_check_module)。

静态资源服务器

Nginx非常适合作为静态资源服务器,因为它处理静态文件的速度非常快,且内存消耗低。

nginx
server {
    listen 80;
    server_name static.example.com;
    # 关闭访问日志,减少磁盘IO(可选)
    access_log off;

    # 设置静态资源根目录
    root /data/www/static;

    location / {
        # 列出目录(生产环境通常关闭)
        autoindex off;
        # 尝试按顺序查找文件,如果都没找到则返回404
        try_files $uri $uri/ =404;
    }

    # 针对图片、字体等设置更长的缓存时间
    location ~* \.(jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ {
        expires 1y; # 设置缓存过期时间为1年
        add_header Cache-Control "public, immutable"; # 告诉浏览器和CDN可以长期缓存
        # 可选:开启防盗链
        # valid_referers none blocked static.example.com *.example.com;
        # if ($invalid_referer) {
        #     return 403;
        # }
    }

    # 针对JS和CSS文件,可以设置较短的缓存,并启用gzip
    location ~* \.(js|css)$ {
        expires 30d;
        add_header Cache-Control "public";
        # 确保gzip对这些文件类型生效
        gzip_static on; # 如果存在 .gz 预压缩文件,直接使用
        gzip_types text/css application/javascript;
    }

    # 禁止访问隐藏文件(如 .git, .env)
    location ~ /\. {
        deny all;
    }
}

关键优化指令

  • expires: 设置Expires和Cache-Control响应头,控制浏览器缓存。
  • try_files: 非常实用的指令,用于按顺序尝试多个文件或路径,最后一个参数可以是状态码或命名location。
  • gzip_static: 优先发送预压缩的 .gz 文件,可以节省实时压缩的CPU开销。需要提前使用 gzip 命令压缩好文件。
  • sendfile: 在 http, server, location 块中开启 sendfile on;,允许Nginx直接在内核空间完成文件传输,跳过用户空间的拷贝,提升传输效率。
  • tcp_nopush: 与 sendfile 配合使用,优化数据包的发送。

防盗链配置

防止其他网站直接链接你的静态资源,消耗你的带宽。

nginx
location ~* \.(jpg|jpeg|png|gif)$ {
    valid_referers none blocked server_names
                   *.yourdomain.com
                   ~\.google\. ~\.baidu\.; # 允许搜索引擎抓取
    if ($invalid_referer) {
        # 可以返回403,或者一个提示图片
        return 403;
        # rewrite ^ /path/to/anti-stealing.jpg break;
    }
}