找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3728

积分

0

好友

512

主题
发表于 4 小时前 | 查看: 2| 回复: 0

来源:juejin.cn/post/7582156410320371722

下面是我在实际使用 Nginx 过程中,发现并总结的四个值得注意的问题,分享出来,希望能帮助大家更好地理解和运用 Nginx。

问题一:路径代理的“/”陷阱与 location 匹配的奥义

先看下面两组配置的细微差别。

第一组:proxy_pass 指令后的斜杠。

# 配置一
location /test {
  proxy_pass 'http://192.186.0.1:8080';
}

# 配置二
location /test {
  proxy_pass 'http://192.186.0.1:8080/';
}

仔细观察,唯一的区别在于 proxy_pass 指令后面的代理地址是否以斜杠 / 结尾。

那么,它们会产生什么不同效果呢?

假设请求的后端接口是 /test/file/getList,这两个配置会产生截然不同的请求结果:

  • http://192.186.0.1:8080/test/file/getList (配置一的结果)
  • http://192.186.0.1:8080/file/getList (配置二的结果)

区别就在于:proxy_pass 后面地址的末尾是否有 /。如果有 /,则表示去除 location 匹配到的路径前缀(这里是 /test);如果没有,则保留该前缀。

实际上,我并不十分推荐这种通过代理地址末尾加不加 / 来控制是否去除前缀的写法。虽然简洁,但对于不熟悉 Nginx 的同学来说,容易造成困惑。

我推荐下面这种更清晰的替代写法,可读性更好:

# 推荐的替代写法
location /test {
  rewrite ^/test/(.*)$ /$1 break;
  proxy_pass 'http://192.186.0.1:8080';
}

通过 rewrite 指令,我们能清晰地看到路径前缀是如何被去除的。简单来说:决定 proxy_pass 后面的地址是否带 /,取决于后端接口是否需要 /test 这个路径。如果后端接口本身包含该路径,代理地址就不应以 / 结尾;如果 /test 是前端为做反向代理拦截而添加的,后端并无此路径,那么代理地址就应该以 / 结尾。

接下来,我们再看第二组区别:location 指令后的斜杠。

# 配置一
location /test {
  proxy_pass 'http://192.186.0.1:8080';
}

# 配置二
location /test/ {
  proxy_pass 'http://192.186.0.1:8080';
}

这次的区别在于 location 指令后面是否直接跟了斜杠 /。那么,这两者有什么区别呢?

答案是:有区别!区别在于匹配规则不同。

  • /test前缀匹配,表示匹配以 /test 开头的所有路径,例如 /test/file/getList/test123 都会被匹配到。
  • /test/ 是更精确的匹配,表示只匹配以 /test/ 开头的路径,例如 /test/file/getList 会被匹配到,但是 /test123/test 不会被匹配到。

我们可以通过下表来更直观地看清区别:

Nginx Location路径匹配规则对比表

如果你仔细观察上表,会发现一个问题:/test//test/abc 这两个请求路径,同时被 location /testlocation /test/ 两个配置都匹配到了。那么,Nginx 会如何选择呢?

答案是:会选择 location /test/

这个问题正好引出了 Nginx 的 location 匹配优先级 问题。借此机会,我们来详细梳理一下 Nginx 的 location 匹配规则。

为了方便记忆,有个口诀:

等号精确第一名
波浪前缀挡正则
正则排队按顺序
普通前缀取最长

解释:

  • 等号 (=) 精确匹配:优先级最高。
  • 前缀优先匹配 (^~):能“挡住”后面定义的正则匹配,直接命中,提升性能。
  • *正则匹配 (~ 区分大小写, `~` 不区分大小写):按照它们在配置文件中出现的顺序**进行匹配,先到先得。
  • 普通前缀匹配 (无符号):按照 最长前缀匹配 原则。

说实话,这个口诀我也记不全,现在也不是背八股文的时代了。遇到具体问题,直接咨询 AI 或者让智能助手帮我修改 nginx.conf 文件,效率更高。不过,理解其背后的原理依然非常重要。

下面通过一个综合性的电商网站 Nginx 配置案例,来帮助大家更好地理解这些规则。

server {
    listen 80;
    server_name shop.example.com;
    root /var/www/shop;

    # ==========================================
    # 1. 精确匹配 (=) - 最高优先级
    # ==========================================
    location = / {
        return 200 "欢迎来到首页 [精确匹配 =]";
        add_header Content-Type text/plain;
    }
    location = /robots.txt {
        return 200 "User-agent: *\nDisallow: /admin/";
        add_header Content-Type text/plain;
    }
    location = /favicon.ico {
        log_not_found off;
        access_log off;
        expires 30d;
    }

    # ==========================================
    # 2. 前缀优先匹配 (^~) - 阻止正则匹配
    # ==========================================
    location ^~ /static/ {
        alias /var/www/shop/static/;
        expires 30d;
        add_header Cache-Control "public, immutable";
        return 200 "静态资源目录 [前缀优先 ^~]";
    }
    location ^~ /uploads/ {
        alias /var/www/shop/uploads/;
        expires 7d;
        return 200 "上传文件目录 [前缀优先 ^~]";
    }
    location ^~ /. {
        deny all;
        return 403 "禁止访问隐藏文件 [前缀优先 ^~]";
    }

    # ==========================================
    # 3. 正则匹配 (~ ~*) - 按顺序匹配
    # ==========================================
    location ~ \.(jpg|jpeg|png|gif|webp|svg|ico)$ {
        expires 30d;
        add_header Cache-Control "public";
        return 200 "图片文件 [正则匹配 ~]";
    }
    location ~* \.(css|js)$ {
        expires 7d;
        add_header Cache-Control "public";
        return 200 "CSS/JS文件 [正则不区分大小写 ~*]";
    }
    location ~* \.(ttf|woff|woff2|eot)$ {
        expires 365d;
        add_header Cache-Control "public, immutable";
        add_header Access-Control-Allow-Origin *;
        return 200 "字体文件 [正则不区分大小写 ~*]";
    }
    location ~ \.php$ {
        # fastcgi_pass unix:/var/run/php-fpm.sock;
        # fastcgi_index index.php;
        return 200 "PHP文件处理 [正则匹配 ~]";
    }
    location ~ \.(bak|backup|old|tmp)$ {
        deny all;
        return 403 "禁止访问备份文件 [正则匹配 ~]";
    }

    # ==========================================
    # 4. 普通前缀匹配 - 最长匹配原则
    # ==========================================
    location /api/v2/ {
        proxy_pass http://backend_v2;
        return 200 "API v2接口 [普通前缀,更长]";
    }
    location /api/v1/ {
        proxy_pass http://backend_v1;
        return 200 "API v1接口 [普通前缀,较短]";
    }
    location /api/ {
        proxy_pass http://backend;
        return 200 "API通用接口 [普通前缀,最短]";
    }
    location /product/ {
        try_files $uri $uri/ /product/index.html;
        return 200 "商品详情页 [普通前缀]";
    }
    location /user/ {
        try_files $uri $uri/ /user/index.html;
        return 200 "用户中心 [普通前缀]";
    }
    location /admin/ {
        auth_basic "Admin Area";
        auth_basic_user_file /etc/nginx/.htpasswd;
        return 200 "管理后台 [普通前缀]";
    }

    # ==========================================
    # 5. 通用匹配 - 兜底规则
    # ==========================================
    location / {
        try_files $uri $uri/ /index.html;
        return 200 "通用匹配 [兜底规则]";
    }
}

针对上面的配置,不同请求 URI 的匹配结果可以参考下表:

Nginx Location匹配优先级综合示例表

第一个问题及其延伸就到这里,我们继续看第二个问题。

问题二:nginx -s reload vs systemctl reload nginx,优雅重启选哪个?

先看下面两个常用的 Nginx 重载配置命令:

# 命令一
nginx -s reload

# 命令二
systemctl reload nginx

上面两个命令都可以重载 Nginx 服务,使新配置生效。但你想过它们之间有什么区别吗?哪个用起来更“优雅”?

答案:有区别!区别在于命令的执行方式和适用场景。

nginx -s reload
这是 Nginx 自带的信号控制命令:

  • 直接向 Nginx 主进程发送 reload 信号。
  • 优雅重启:不会中断现有连接,平滑加载新配置。
  • 需要 nginx 命令在系统的 PATH 环境变量中,或者使用完整路径(如 /usr/sbin/nginx -s reload)。
  • 这是 Nginx 原生的重启方式。

systemctl reload nginx
这是通过 systemd 系统服务管理器来执行的命令:

  • 通过 systemd 管理 Nginx 服务。
  • 同样会执行优雅重启,平滑加载新配置。
  • 需要系统为 systemd 环境,适用于使用 systemd 管理服务的现代 Linux 发行版(如 CentOS 7/8, RHEL 7/8, Ubuntu 16.04+)。这也是现代 Linux 系统的推荐方式。

简单对比其他相关命令:

  • nginx -s stop 等价 systemctl stop nginx
  • nginx -s quit 等价 systemctl stop nginx (也是优雅停止)
  • nginx -t (测试配置语法) - 这个没有直接的 systemctl 对应命令

systemctl 下相关常用命令:

# 设置开机自启
systemctl enable nginx

# 启动服务
systemctl start nginx

# 检查服务状态
systemctl status nginx

# 停止服务
systemctl stop nginx

# 重启服务(会中断连接)
systemctl restart nginx

# 平滑重载配置(不中断服务)-- 对应 nginx -s reload
systemctl reload nginx

# 检查配置文件语法(这是调用nginx二进制文件的功能)
nginx -t

在服务器上,一个优雅且安全的操作组合通常是:

# 1. 先测试配置语法是否正确
nginx -t

# 2. 如果配置正确,再使用systemd平滑重载
systemctl reload nginx

# 3. 检查服务状态
systemctl status nginx

# 4. 如果systemctl命令失败或不存在,再回退到直接方式
sudo nginx -s reload

总结: 我们不能只知道这两个命令都能用,却不关心它们的区别。对于使用现代 Linux 发行版(带 systemd)的服务器,推荐优先使用 systemctl 来管理 Nginx 服务,因为这是系统管理服务的标准方式,更规范。在本地开发环境或者没有 systemd 的环境下,则可以使用 nginx -s reload 这种直接方式。

问题三:离线环境如何安装 Nginx?

我们通常接触的都是可以联网的服务器,直接用包管理器安装 Nginx 即可。以 Ubuntu/Debian 为例:

# 更新包列表
sudo apt update
# 安装 Nginx
sudo apt install nginx
# 启动 Nginx
sudo systemctl start nginx
# 设置开机自启
sudo systemctl enable nginx

但是,如果遇到一台无法连接外网的服务器,该如何安装 Nginx 呢?由于服务器可能有不同的 CPU 架构(如 x86_64, ARM64),需要准备对应的安装包。

方案一:使用官方预编译包

首先,查看服务器的架构信息:

# 查看当前系统架构
uname -m
# 输出示例:
# x86_64    -> Intel/AMD 64位
# aarch64   -> ARM 64位
# armv7l    -> ARM 32位

# 查看系统发行版和版本
cat /etc/os-release

然后,从 Nginx 官网或其他可信源下载对应系统和架构的预编译包。

对于 x86_64 架构 (CentOS/RHEL 7 示例):

wget http://nginx.org/packages/centos/7/x86_64/RPMS/nginx-1.24.0-1.el7.ngx.x86_64.rpm

对于 ARM64 架构 (Ubuntu 22.04 示例):

wget http://nginx.org/packages/ubuntu/pool/nginx/n/nginx/nginx_1.24.0-1~jammy_arm64.deb

将下载好的包上传到离线服务器,然后使用对应包管理工具安装:

# 对于 RPM 包 (CentOS/RHEL/Fedora)
cd /tmp
sudo rpm -ivh nginx-*.rpm

# 对于 DEB 包 (Ubuntu/Debian)
cd /tmp
sudo dpkg -i nginx-*.deb

安装完成后,启动并验证服务:

sudo systemctl start nginx      # 启动
sudo systemctl enable nginx     # 开机自启
sudo systemctl status nginx     # 查看状态

nginx -v                       # 查看版本
curl http://localhost          # 测试访问

方案二:源码编译安装

这种方式一般不推荐,除非有特殊需求(如需要集成特定第三方模块或使用最新特性)。源码编译过程相对复杂,涉及解决依赖、配置编译选项、make && make install 等步骤。通常这类任务更适合交给对此有经验的后端或运维工程师处理。

问题四:如何配置 Nginx 正确部署 WebAssembly (WASM) 应用?

当你使用 Unity 3D 等引擎开发并导出 WebAssembly 项目后,使用 Nginx 部署时,可能会遇到如下错误:

Failed to load module script: The server responded with a non-JavaScript MIME type of "application/wasm".

或者提示 content-type ... not ... wasm

这通常是因为 Nginx 没有正确配置 application/wasm 的 MIME 类型,导致浏览器无法识别 WASM 文件。

要正确部署 WASM 应用,需要进行以下两部分配置:

第一部分:配置正确的 MIME 类型

进入 Nginx 的安装目录,找到 mime.types 文件(通常在 /etc/nginx/mime.types/usr/local/nginx/conf/mime.types),在其中添加或确保包含以下配置:

application/wasm        wasm;

第二部分:针对 WASM 应用的特殊 Nginx 配置

以下是一个可直接使用或参考的完整配置示例,你只需要修改服务器名、文件根目录等参数即可。

server {
    listen 80;
    server_name your-domain.com;  # 修改为你的域名或IP

    # Unity WebGL 构建文件的根目录
    root /var/www/unity-webgl;
    index index.html;

    charset utf-8;

    # ========== MIME 类型配置(核心部分) ==========
    # WASM文件(未压缩)
    location ~ \.wasm$ {
        types {
            application/wasm wasm;
        }
        add_header Cache-Control "public, max-age=31536000, immutable";
        add_header Access-Control-Allow-Origin *;
    }
    # WASM文件(Gzip压缩)
    location ~ \.wasm\.gz$ {
        add_header Content-Encoding gzip;
        add_header Content-Type application/wasm;
        add_header Cache-Control "public, max-age=31536000, immutable";
        add_header Access-Control-Allow-Origin *;
    }
    # WASM文件(Brotli压缩)
    location ~ \.wasm\.br$ {
        add_header Content-Encoding br;
        add_header Content-Type application/wasm;
        add_header Cache-Control "public, max-age=31536000, immutable";
        add_header Access-Control-Allow-Origin *;
    }

    # Data文件(Unity资源文件,配置类似)
    location ~ \.data$ {
        types {
            application/octet-stream data;
        }
        add_header Cache-Control "public, max-age=31536000, immutable";
    }
    location ~ \.data\.gz$ {
        add_header Content-Encoding gzip;
        add_header Content-Type application/octet-stream;
        add_header Cache-Control "public, max-age=31536000, immutable";
    }
    location ~ \.data\.br$ {
        add_header Content-Encoding br;
        add_header Content-Type application/octet-stream;
        add_header Cache-Control "public, max-age=31536000, immutable";
    }

    # JavaScript 文件
    location ~ \.js$ {
        types {
            application/javascript js;
        }
        add_header Cache-Control "public, max-age=31536000, immutable";
    }
    location ~ \.js\.gz$ {
        add_header Content-Encoding gzip;
        add_header Content-Type application/javascript;
        add_header Cache-Control "public, max-age=31536000, immutable";
    }
    location ~ \.js\.br$ {
        add_header Content-Encoding br;
        add_header Content-Type application/javascript;
        add_header Cache-Control "public, max-age=31536000, immutable";
    }

    # Framework、Loader JS 和 Symbols JSON 文件(配置示例,可根据需要调整)
    location ~ \.framework\.js(\.gz|\.br)?$ {
        if ($uri ~ \.gz$) {
            add_header Content-Encoding gzip;
        }
        if ($uri ~ \.br$) {
            add_header Content-Encoding br;
        }
        add_header Content-Type application/javascript;
        add_header Cache-Control "public, max-age=31536000, immutable";
    }
    # ... 类似配置可以继续为 .loader.js, .symbols.json 添加

    # ========== 静态资源缓存配置 ==========
    location /StreamingAssets/ {
        add_header Cache-Control "public, max-age=31536000, immutable";
    }
    location /Build/ {
        add_header Cache-Control "public, max-age=31536000, immutable";
    }
    location /TemplateData/ {
        add_header Cache-Control "public, max-age=86400";
    }

    # 图片、CSS文件
    location ~* \.(jpg|jpeg|png|gif|ico|svg)$ {
        add_header Cache-Control "public, max-age=2592000";
    }
    location ~* \.css$ {
        add_header Content-Type text/css;
        add_header Cache-Control "public, max-age=2592000";
    }

    # ========== HTML 文件(不缓存,确保更新生效) ==========
    location ~ \.html$ {
        add_header Cache-Control "no-cache, no-store, must-revalidate";
        add_header Pragma "no-cache";
        add_header Expires "0";
    }

    # 根路径兜底规则
    location / {
        try_files $uri $uri/ /index.html;
    }

    # ========== 性能优化:Gzip 压缩 ==========
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_min_length 1024;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/json
        application/javascript
        application/x-javascript
        application/xml
        application/xml+rss
        application/wasm
        application/octet-stream;

    # ========== 安全配置 ==========
    # 禁止访问隐藏文件
    location ~ /\. {
        deny all;
    }
    # 禁止访问临时/备份文件
    location ~ ~$ {
        deny all;
    }
    # 添加安全响应头
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";
    add_header X-Frame-Options "SAMEORIGIN";
}

总结:配置 WASM 应用的核心要点

  1. MIME 类型是基础:必须在 mime.types 文件中添加 application/wasm wasm;,这是浏览器识别 WASM 文件的前提。
  2. 详细的文件类型处理:在 nginx.conf 中,需要为 WASM (.wasm)、Data (.data)、JS (.js) 等文件分别配置未压缩及压缩版本(gzip/br)的处理规则,正确设置 Content-TypeContent-Encoding 响应头。
  3. 合理的缓存策略:为静态资源(如 StreamingAssets、Build 目录)设置长时间的缓存(immutable),提升重复访问速度。而为 HTML 文件禁用缓存,确保用户总能获取到最新版本。
  4. 性能与安全:开启 Gzip 压缩提升传输效率,并添加基本的安全响应头(如 XSS 防护、禁止内容嗅探等)来加固服务器。

在云栈社区的运维 & 测试板块,你可以找到更多关于服务器配置、性能优化和稳定性保障的深度讨论与最佳实践。希望这四个从实战中总结出的 Nginx 问题,能帮助你在配置和管理 Web 服务器时更加得心应手。




上一篇:深入解析OpenClaw:它与Skills、Agent、RAG及MCP的关系与架构全景
下一篇:RT-Thread操作系统在NXP MCX平台实现智能头盔多传感器采集与华为云IoT上报
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-14 08:27 , Processed in 0.439190 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表