侧边栏壁纸
  • 累计撰写 59 篇文章
  • 累计创建 25 个标签
  • 累计收到 27 条评论

最推荐的一份 Nginx 配置清单,反向代理、限流、SSL、负载均衡全都有

heshaohua
2025-07-23 / 0 评论 / 0 点赞 / 13 阅读 / 4,253 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2025-08-02,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

Nginx 的强大,不在于它装得多快,而在于它配得有多灵活。前面我们已经完成了多种环境下的安装,接下来真正的重点来了:配置。几乎所有核心功能,都是通过精心编写的 .conf 文件实现的。学会配置,才算真正学会了用 Nginx。

Nginx 能反向代理、限流、负载均衡,全靠它的 .conf 文件来实现。本文将带入生产环境常用的实际场景,去解决如何配置 conf ,干货文章建议收藏。

一、负载均衡

单节点的反向代理配置,将 HTTP 请求转发的 Tomcat 服务器,但是只能代理到单节点,无法在集群中使用。

server {
    location /testapi/ {
            proxy_pass     http://178.168.1.10:9120/;
            index   /;      # index.html index.htm;
            proxy_set_header Host $host;
            # 表示与这个nginx 直接通信的IP, 如果这个是第一层代理,这将是最真实的客户端IP
            proxy_set_header X-Real-IP $remote_addr;
            # 表示与这个nginx 直接通信的Port, 如果这个是第一层代理,这将是最真实的客户Port
            proxy_set_header X-Real-Port $remote_port;
            # NGINX 在转发请求时会将客户端的 IP 地址添加到 X-Forwarded-For 头部
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            client_max_body_size 8M;
            client_body_buffer_size 128k;
            proxy_connect_timeout       10s; 
            proxy_send_timeout          60s;
            proxy_read_timeout          60s;
    }
}

而负载均衡配置 用于将 HTTP 请求分布到多个节点的 Tomcat 服务器;负载均衡功能支持自动下线,当某一台节点服务端返回 500,则会自动下线该节点不再访问。负载均衡的配置可以单独写成 conf 文件。

upstream openserver-api {
      # ip_hash; 根据访问ip的hash结果分配 
      # least_conn;请求分配到连接数最少的服务
      # fair; 请求分配到后端响应的时间最短
      server 127.0.0.1:8900 weight=100 max_fails=10 fail_timeout=15s;
      server 127.0.0.1:8901 weight=100 max_fails=10 fail_timeout=15s;
}

server {
  location /test-api/{
      proxy_pass     http://openserver-api/;
      index   /;      # index.html index.htm;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Real-Port $remote_port;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      client_max_body_size 8M;
      client_body_buffer_size 128k;
  }
}

上面是按权重轮询服务,在15秒(fail_timeout)内该服务器出现了10次(max_fails)失败,负载均衡器就会停止向它发送请求。生产环境就是使用这样平均分配的策略;

除了轮询策略,还有其他负载均衡策略。在注释上标注了,去掉注释就可以直接使用,但是这些策略都不是平均分配的,需要自己去控制流量的大小,如果没有特殊的情况,还是建议轮询服务。

二、静态资源访问

2.1 配置网页

现在前端开发项目已经不是简单的 html 页面了,多是使用 Vue 等框架开发的,这里给出 Vue 前端项目的页面配置。

server {
  location /page/ {
    alias  /home/user/web/page/dist/;
    try_files $uri $uri/ /page/index.html;
  }
}

当浏览器访问 https://example.com/page 时,nginx 会代理到 /home/user/web/page/dist/目录,此时,$uri 就表示 /page

try_files 会先检查 alias 目录下 page文件是否存在,存在则返回该文件;

然后会检查 page/目录是否存在,存在则返回 page/ 目录下的 index.xml(page 不带斜杠表示文件,page/ 带斜杠表示目录);

最后如果都找不到,则返回 /page/index.html ,此时 /page/表示相对目录,和上面的 location /page/表示一个意思,即 alias 表示的路径,所以 /page/index.html 完整的路径表示 /home/user/web/page/dist/index.html

alias 和 root 的区别?

alias 是一个目录别名的定义,可以直接替换,比如 /page/ 就表示 /home/user/web/page/dist/

root 是最上层目录的定义,访问 /page/ 则表示 访问 root 后面的目录 + /page/,即 /home/user/web/page/dist/page/

2.2 配置验证文件

在很多第三方页面嵌入到主应用,或者跳转第三方域名的时候,往往需要先提前校验域名是否合法。

一般做法是将一个文本校验文件放到域名根目录,例如要访问 https://example.com, 必须要求 https://example.com/mK49l7IJfF.txt 能访问,这里的 mK49l7IJfF.txt 就需要放到域名根目录,怎么做呢?

# 根路径,在 localtion 内部嵌套
location / {
    alias  /home/user/web/page/dist/;
    try_files $uri $uri/ /index.html;

    location = /mK49l7IJfF.txt {
        alias /home/user/web/index/dist/mK49l7IJfF.txt;
    }    
}

# 普通路径, 在 localtion 内部嵌套
location /page/ {
    alias  /home/user/web/page/dist/;
    try_files $uri $uri/ /page/index.html;

    location = /page/mK49l7IJfF.txt {
        alias /home/user/web/index/dist/mK49l7IJfF.txt;
    }    
}

如果文件一直访问不通 返回404,考虑 mK49l7IJfF.txt 文件是否 Nginx 存在权限不够,无法访问。

2.3 页面禁止缓存

为了提供响应速度,浏览器访问页面会有缓存,页面无法及时更新。如何彻底浏览器禁止缓存?需要在响应的请求头中加入禁止缓存头。

location / {
    add_header Cache-Control "no-cache, no-store, must-revalidate";
    add_header Pragma "no-cache";
    add_header Expires 0;
}

2.4 配置404页面

可以通过指定error_page 404 触发显示 404页面,location 一定要配置在 server{} 目录下,否则不生效。

另外页面中如果存在图片等资源,请注意路径参数等问题。

http{
    ...
    # 设置全局的 404 错误页面
    error_page 404 /404.html;
    ...
}

server{
    ...
   location = /404.html {
       root /home/user/web/404/;
       # 限制外部访问
       internal;  
   }
   ...
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>404 - Page Not Found</title>
    <style>
        body { font-family: Arial, sans-serif; }
        h1 { color: red; }
    </style>
</head>
<body>
    <h1>404 - Page Not Found 当前页面不存在</h1>
</body>
</html>

三、隐藏版本号

在浏览器响应头里有 Server 项,这里返回的是当前服务 web 服务器的名称,如 Nginx、Kong 等。

但是这里不应该把服务器的版本号返回出来,万一版本有漏洞,会被针对攻击。所以如何隐藏版本号呢?
640

# 1、关闭 http{} 的 server_tokens
server_tokens off;


#2、修改 ../nginx/conf/fastcgi_params 
fastcgi_params SERVER_SOFTWARE 
nginx/$nginx_version
# 改为 
fastcgi_params SERVER_SOFTWARE nginx

fastcgi_params 这个文件里的存放的都是 Nginx 环境变量。

四、日志管理

4.1 日志定时分片

请直接参考这篇文章:别让日志撑爆磁盘!Nginx日志优化实战:自动分片、定时归档、轻松管理亿级日志

4.2 日志 JSON 格式化

将 Nginx 的日志格式化为 JSON,可以修改 log_format 指令,将日志字段转化为 JSON 格式。

JSON 格式的日志,更适合日志聚合和后续处理,比如 ELK, Flume 等工具。

JSON 格式化配置:

log_format json '{"remote_addr": "$remote_addr", "connection": "$connection", "connection_requests": "$connection_requests", "remote_user": "$remote_user", "time_local": "$time_local", "request_length": "$request_length", "request": "$request", "status": "$status", "request_time": "$request_time", "upstream_response_time": "$upstream_response_time", "body_bytes_sent": "$body_bytes_sent", "content_length": "$content_length", "http_x_forwarded_for": "$http_x_forwarded_for", "upstream_addr": "$upstream_addr", "http_referer": "$http_referer", "http_user_agent": "$http_user_agent"}';

确保将此配置添加到 http 块中,并设置 access_log 指令以使用这个 JSON 格式:

http {
    log_format json '{"remote_addr": "$remote_addr", "connection": "$connection", "connection_requests": "$connection_requests", "remote_user": "$remote_user", "time_local": "$time_local", "request_length": "$request_length", "request": "$request", "status": "$status", "request_time": "$request_time", "upstream_response_time": "$upstream_response_time", "body_bytes_sent": "$body_bytes_sent", "content_length": "$content_length", "http_x_forwarded_for": "$http_x_forwarded_for", "upstream_addr": "$upstream_addr", "http_referer": "$http_referer", "http_user_agent": "$http_user_agent"}';

  # 日志格式可以引用main ,也可以引用 json
    access_log /var/log/nginx/access.log json;
}

日志将记录为 JSON 格式,示例如下:

{
    "remote_addr": "180.101.49.58",
    "connection": "3",
    "connection_requests": "1",
    "remote_user": "-",
    "time_local": "18/Jan/2025:08:02:38 +0000",
    "request_length": "1237",
    "request": "POST /api/queryList HTTP/1.1",
    "status": "200",
    "request_time": "0.112",
    "upstream_response_time": "0.111",
    "body_bytes_sent": "1574",
    "content_length": "2",
    "http_x_forwarded_for": "-",
    "upstream_addr": "180.101.49.58:18105",
    "http_referer": "http://example.com:18105/query/?token=erwerwer&activityId=werwer&level=100",
    "http_user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0"
}

五、灰度发布

如何基于 Nginx 设计灰度发布呢?请看这篇文章。主要运用的配置是负载均衡和权重策略。轻松搞定服务灰度发布!基于Nginx的流量分发与版本控制实战指南

六、拓展模块

6.1、限流功能

参考下面这篇文章: 高并发场景下接口防刷!3 分钟配置 Nginx 基于 IP 限流实战指南

6.2、禁止区域流量

简单的使用 deny 和 allow 配合使用。放在 http 、server、location 中都可以。

server {
    listen80;
    server_name example.com;

    # 允许特定 IP 地址
    allow192.168.1.1;
    allow203.0.113.2;

    # 拒绝所有其他 IP 地址
    deny all;

    location / {
        # 其他配置...
    }
}

server {
    listen80;
    server_name example.com;

    # 禁止多个 IP 地址访问
    deny192.168.1.100;
    deny203.0.113.50;

    location / {
        # 其他配置...
    }
}

也可以使用文件批量实现 IP 限制,然后把语法文件写入到 limitip.conf 文件中

server {
    listen 80;
    server_name example.com;
    include /nginx/conf/limitip.conf;
    ...
}
deny 192.168.1.100;
deny 203.0.113.50;

那只允许中国的 IP 访问怎么做呢?

http {
    # 定义中国 IP 范围,默认所有 IP 都拒绝
    geo$allowed_country {
        default0;  # 默认拒绝所有 IP
        # 以下为中国的 IP 地址段示例,请根据实际情况更新
        101.0.0.0/8 1;
        103.0.0.0/8 1;
        106.0.0.0/8 1;
        # 其他中国 IP 地址段...
    }

    server {
        listen80;
        server_name example.com;

        location / {
            # 如果不是中国 IP,返回 403 Forbidden
            if ($allowed_country = 0) {
                return403;  # 非中国 IP 返回 403 Forbidden
            }

            root /usr/share/nginx/html;
            index index.html;
        }
    }
}

6.3、配置各种响应头

配置响应头是为了告诉浏览器某些操作,比如禁止缓存、禁止iframe嵌套等,Nginx 作为 Web 服务器,支持设置响应头。加在 server 里可以全局统一

location / {
    # 禁止浏览器缓存
    add_header Cache-Control "no-cache, no-store, must-revalidate";
    add_header Pragma "no-cache";
    add_header Expires 0;

    #该页面不允许在frame中展示,另外还有 SAMEORIGIN(表示该页面可以在相同域名页面的frame中展示),
    # ALLOW-FROM url(页面可以在指定来源的frame中展示)
    # 这个头加之前需要和前端同步
    add_header X-Frame-Options:DENY

    #启用XSS保护;
    add_header X-Xss-Protection: 1;
    add_header X-Xss-Protection: mod=block;

    # 严格按照 Content-Type指定的类型加载,直接猜测
    add_header X-Content-Type-Options "nosniff";

    #指定哪些域名可以访问当前资源
    add_header Access-Control-Allow-Origin "*";

    #防止浏览器在下载文件时自动执行内容(如 JS 文件)
    add_header X-Download-Options "noopen";

    # 默认只允许加载与页面相同源的资源,帮助防止跨站脚本攻击(XSS)
    add_header Content-Security-Policy "default-src 'self';" always;

    # 启用 HSTS,强制所有连接使用 HTTPS,禁止304 跳转
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

}

6.4、防止DNS欺骗恶意域名解析

DNS 欺骗可以将非法域名解析到服务器上,导致服务器被恶意访问,在原有配置的上面可以添加一个默认 server ,当其他域名或通过 IP 访问的时候,由于匹配不到 server,就会走默认的 server, 返回444。

444 是 Nginx 服务器扩展的 HTTP错误状态码,服务器不向客户端返回任何信息,并关闭连接, 断开客户端和服务器的连接,防止恶意软件攻击威胁。通过此方式直接通过IP 将无法访问。

server {
    listen 80 default;
    server_name _;  # 匹配所有没有匹配到其他虚拟主机的请求
    return 444;     # 直接返回 444,关闭连接
}

Q&A

Q1:remote_addr 和 http_x_forwarded_for的 区别是什么?

A: remote_addr 表示上游真实IP。假设用户和服务器间有多级代理,那么 remote_addr 就表示最接近服务器的那个代理。

remote_addr 无法伪造,因为建立 TCP 连接需要三次握手,如果伪造了源 IP,无法建立 TCP 连接,更不会有后面的 HTTP 请求。

如果上游是 nginx,那么 remote_addr 只能表示 nginx IP,而不是客户 IP;http_x_forwarded_for 格式为 X-Forwarded-For: client, proxy1,它是一个请求头,表示多级代理的IP,可以完整的展示客户端到服务器间的代理链,每级代理需要将上级客户端IP(remote_addr)加到 X-Forwarded-For 后面。

所以 X-Forwarded-For 不包含最后一级代理,最后一级代理直接通过 remote_addr 获取。

计算方式:X-Forwarded-For = 最近代理获取到的 X-Forwarded-For + 最近代理获取到的 remote_addr。

所以服务如果通过 Nginx 代理访问,就要禁止直接访问服务,这是为了保证上级代理不会被伪造,否则客户端直连会伪造 IP 数据。必须使用从 TCP 连接中得到的 Remote Address 才是真实的,其他均不可信!

请一定记住:

1、使用 Nginx 就要禁掉直连服务,保证上级代理不会被伪造请求头;
2、单 Nginx 转发,X-Real-IP 和 X-Forwarded-For 最后一节为真实客端 IP;
3、多 Nginx 转发,X-Real-IP 和 X-Forwarded-For 最后一节为倒数第二级代理IP,建议直接将第一级转发;

Q2: Nginx 访问异常,有时能访问到 有时候访问不到?

排查发现:
1、域名本身过期;
2、域名证书过期;
3、域名没有备案;

只要服务器在国内,所有的域名,包括 hosts 自定义的的域名在国内都需要备案。

Q3: 走 Nginx 压测后端服务出现问题?

Nginx 负载到两个节点上,流量直接走Nginx ,使用 Jmter 压测服务,接口一直响应超时返回502,QPS 上不去。

查询 Nginx 日志发现异常,从日志可以看出,Nginx 获取上游响应超时,但是直接压测服务不走 Nginx 的话,就没有这个问题,这说明服务端没有问题,而是 Nginx->api 这个路径出现问题,Nginx 负载出现问题,请求还没到 Tomcat ?

报错日志如下:

1、upstream server temporarily disabled while connecting to upstream
2、(upstream timedout(110: Connection timedout)whilereading response header from upstream)
3、no live upstreams while connecting to upstream,

解决方案如下:

1、填加超时配置:max_fails 重试次数,fail_timeout = 超时重试时间,keepalive 保持长连接时间,(Http1.1,默认支持长连接,可以减少 TCP 创建)

# 10秒内连续出现超过2次 
server  127.0.0.1:9501 max_fails=2 fail_timeout=10s weight=1;
server  127.0.0.1:9502 max_fails=2 fail_timeout=10s weight=1;
keepalive 10;

2、nginx->API 链路大量 TCP 被断开,检查 TCP 连接是否存在异常。

# 统计TCP状态数量
netstat -n | awk '/^tcp/ {++state[$NF]} END {for(key in state) print key,"\t",state[key]}'

# 返回如下结果
FIN_WAIT_1       1
CLOSE_WAIT       8
TIME_WAIT        44
ESTABLISHED      42
location ^~ /xxxxxx/ {
    ...
    # 强制走http 1.1,默认会采用 Keep-Alive
    proxy_http_version 1.1;
    ...
}

3、排除nginx 所在的服务器内核参数设置太小导致丢包的可能性

0

评论区