nginx代码分析之(二)——Empty Gif是如何工作的

本文将探讨nginx中的Empty Gif模块,详细分析其如何在服务器中注册和初始化,以及在实际应用中的具体工作流程。通过模块注册和初始化过程,揭示nginx处理请求的内部机制。
访问新浪时,时常会有一些网页返回空白(但不是“此页无法显示”),从浏览器的信息中可以知道此时服务器返回了一个1×1的空白gif图片。

这实际上是 nginx实现的, nginx有一个名为Empty Gif的module,专门负责此项工作。

由于这个module比较简单,我们就先从它入手,来看看 nginx的模块实现。

模块注册

Empty Gif这个module只有一个文件—— ngx_http_empty_gif_module.c

这个文件比较简单,一开始定义并初始化了3个变量。

static ngx_command_t  ngx_http_empty_gif_commands[] = {...};
static ngx_http_module_t  ngx_http_empty_gif_module_ctx = {...};
ngx_module_t  ngx_http_empty_gif_module = {...};

其中只有ngx_http_empty_gif_module是非静态的,我将暂时将其称为module主结构变量,
而其余两个变量都可以由它访问到。

但是如果继续查看 nginx的源码,会发现没有其他地方引用ngx_http_empty_gif_module,
那这个module是怎么注册并应用起来的呢?

如果熟悉 Apache的代码,会发现这和 Apache 2.0的module机制非常类似——每个module都对应到一个module主结构变量,通过这个主结构变量可以访问到这个module的其他内容,该module所有的函数也用函数指针的方式存放在这些结构变量中。

而且 Apache同样没有其他地方的代码引用到module主结构变量。这是因为module不是必须的,该module在某一个特定的编译版本里是可以不存在的。因此一个module是否有效,不是通过代码来决定,而是通过编译选项来实现。

nginx代码的auto目录中,有一个名为sources的文件,根据编译选项(configure的参数)的不同,m4宏变量HTTP_MODULES的值会发生变化:

如果指定了使用empty gif模块(默认就是使用了),则最终m4宏变量HTTP_MODULES的值可能如下:
HTTP_MODULES="ngx_http_module /
              ngx_http_core_module /
              ngx_http_log_module /
              ngx_http_upstream_module /
              ngx_http_empty_gif_module "

注意:这里的ngx_http_empty_gif_module字符串对应了ngx_http_empty_gif_module.c文件中的Module主结构变量名。

编译之前的configure结束后,会在objs目录下生成一个名为ngx_modules.c的文件,此文件的内容如下:
#include <ngx_config.h>
#include <ngx_core.h>

extern ngx_module_t  ngx_core_module;
extern ngx_module_t  ngx_errlog_module;
extern ngx_module_t  ngx_conf_module;
...
extern ngx_module_t  ngx_http_empty_gif_module;
...

ngx_module_t *ngx_modules[] = {
    &ngx_core_module,
    &ngx_errlog_module,
    &ngx_conf_module,
    ...
    &ngx_http_empty_gif_module,
    ...
    NULL
};

在此生成了对ngx_http_empty_gif_module变量的引用,并将其放到了ngx_modules表中,
通过相关函数可以进行存取。

这样,在编译时就完成了Empty Gif模块注册的过程。

模块的初始化和应用

初始化一般都是根据配置文件的内容来进行,但和我们一般写程序的做法不同—— nginx并没有在一个统一的地方处理所有的配置,而是让每个模块负责处理自己的配置项,如果没有编译这个模块,则其对应的配置项就无法处理,这也是又一个和Apache的相似之处。

nginx使用了ngx_command_t结构来描述某一个模块对应的配置项及处理函数。

以Empty Gif模块为例:
static ngx_command_t  ngx_http_empty_gif_commands[] = {

    { ngx_string("empty_gif"),
      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
      ngx_http_empty_gif,
      0,
      0,
      NULL },

      ngx_null_command
};

上面的定义表明:
1.  Empty Gif模块只处理一个配置项——“empty_gif”
2.  这个配置是一个Location相关的配置(NGX_HTTP_LOC_CONF),
     即只有在处理某一个URL子集,如 /test_[0-9]*.gif时才生效。
  
     实际的配置文件可能如下:
      location ~ /test_[0-9].gif {
            empty_gif;
      }
3.  这个配置项不带参数(NGX_CONF_NOARGS)
4.  配置处理函数是ngx_http_empty_gif

ngx_http_empty_gif函数的实现很简单:

static char *
ngx_http_empty_gif(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_core_loc_conf_t  *clcf;

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
    clcf->handler = ngx_http_empty_gif_handler;

    return NGX_CONF_OK;
}

ngx_http_conf_get_module_loc_conf是一个宏,用于获得Location相关的配置表cf中ngx_http_core_module对应的项,获取之后,Empty Gif模块将自己的处理函数挂到了ngx_http_core_module对应的handler上。

这样, nginx在处理HTTP请求时,如果发现其URL匹配到Empty Gif所属的Location,
如URL(/test_1.gif)匹配到Location(/test_[0-9].gif),
则使用ngx_http_empty_gif作为处理函数,这个函数直接向浏览器写回一幅1×1的空白gif图片。
我们现在有了关键线索: ```bash curl -I --http1.1 https://szrengjing.com/jsxc/dist/jsxc.bundle.min.js ``` 返回: ``` curl: (52) Empty reply from server ``` 这意味着: > ❌ 即使降级到 HTTP/1.1,Nginx 也没有返回任何响应头或内容。 结合之前 `index.html` 能正常访问(✅),但 `.js` 文件完全无响应(❌)—— 这不是压缩或协议问题,而是 **Nginx 配置中对静态资源路径的处理存在严重错误**。 --- ## 🔍 排查思路:为什么同一个目录下 HTML 可访问,JS 不可? | 可能原因 | 分析 | |--------|------| | ✅ `index.html` 能访问 | → 主站点配置生效,SSL、root、server_name 正确 | | ❌ `.js` 文件无响应 | → 极可能是 **PHP 处理规则误匹配了 `.js` 文件**,导致请求被转发给 PHP-FPM,而 PHP 没有输出任何内容 → “Empty reply” | 这是 **宝塔面板用户最常见的陷阱之一!** --- ## 🧩 典型错误配置(在宝塔中常见) 查看你的 Nginx 配置是否包含以下危险片段: ```nginx location ~ \.php$ { fastcgi_pass unix:/tmp/php-cgi.sock; fastcgi_index index.php; include fastcgi.conf; } # ⚠️ 危险!这个规则会把 .js 当作 PHP 执行 location ~ .*\.php($|/) { fastcgi_pass unix:/tmp/php-cgi.sock; include fastcgi.conf; } ``` 更致命的是有些用户添加了: ```nginx # ❌ 错误规则:让所有文件都经过 PHP 处理 location ~* \.(js|css|html)$ { include /www/server/panel/vhost/nginx/proxy/szrengjing.com/*.conf; } ``` 👉 如果你加了类似规则,会导致 `.js` 文件被当作“动态脚本”交给 PHP 执行。 而 `jsxc.bundle.min.js` 显然不是 PHP 脚本 → PHP 解释器无法执行 → 返回空响应 → `Empty reply` --- ## ✅ 第一步:检查是否有错误的 PHP 匹配规则 运行命令搜索可能影响 `.js` 的配置: ```bash grep -r "\.js" /www/server/panel/vhost/nginx/szrengjing.com.conf ``` 重点关注输出中是否有如下危险模式: ```nginx location ~ \.js { fastcgi_pass ... # ❌ 绝对不能有! } if ($request_filename ~* \.js$) { rewrite ... # ❌ 也不该有 } ``` --- ## ✅ 第步:检查是否启用了“反向代理”或“防跨站”功能干扰 宝塔有个“防跨站攻击”功能(open_basedir 控制),它会在每个站点自动插入一段配置,有时会破坏静态文件服务。 ### 查看当前配置顶部是否有: ```nginx include /www/server/panel/vhost/nginx/php-cgi-szrengjing.com.conf; ``` 这类文件可能包含: ```nginx location ~* ^/(uploads|upload|static|data|images|js|css)/ { expires 30d; location ~ \.php$ { fastcgi_pass unix:/tmp/php-cgi.sock; } } ``` ⚠️ 问题来了:这个嵌套 `location ~ \.php$` 是安全的,但如果写成: ```nginx location ~ \.(php|js|css)$ { # ❌ 把 js 加进去了! fastcgi_pass ... } ``` → 就会导致 `.js` 被 PHP 处理! --- ## ✅ 解决方案:添加显式的静态资源排除规则 修改你的站点配置,确保 `.js`, `.css`, `.png` 等静态文件 **不被任何 PHP 规则捕获** ### 编辑配置文件 ```bash vim /www/server/panel/vhost/nginx/szrengjing.com.conf ``` ### 在 `server` 块顶部添加以下内容(优先级最高): ```nginx # ✅ 显式声明静态资源直接由 Nginx 提供,禁止交给 PHP location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|txt|pdf)$ { add_header Cache-Control "public, max-age=31536000"; expires 1y; access_log off; log_not_found off; # 确保不会进入 PHP 处理 try_files $uri =404; } ``` > 💡 注意:`try_files $uri =404;` 表示“如果文件存在就返回,否则 404”,且不进入后续 PHP 处理流程。 ### 同时检查并删除以下危险代码: ```nginx # ❌ 删除或注释掉这些危险规则 # location ~ \.js { # fastcgi_pass ... # } # if ($request_filename ~* \.(js|css)$) { # rewrite ^/(.*)$ /index.php/$1 last; # } ``` --- ## ✅ 第三步:重启 Nginx 并测试 ```bash systemctl restart nginx ``` 然后再次测试: ```bash curl -I --http1.1 https://szrengjing.com/jsxc/dist/jsxc.bundle.min.js ``` 🎯 期望结果: ``` HTTP/1.1 200 OK Server: nginx Content-Type: application/javascript Content-Length: 2508058 Connection: keep-alive Cache-Control: public, max-age=31536000 Expires: Wed, 31 Dec 2025 23:59:59 GMT ``` ✅ 成功! --- ## ✅ 补充建议:为 `/dist/` 目录单独设置安全规则 ```nginx # 安全地提供 JSXC 静态资源 location /jsxc/dist/ { alias /www/wwwroot/szrengjing.com/jsxc/dist/; # 禁止任何 PHP 执行 location ~ \.php$ { deny all; } expires 1y; add_header Cache-Control "public, immutable"; } ``` 这样可以防止未来误操作导致安全风险。 --- ## ✅ 最终验证流程总结 ```bash # 1. 添加静态文件白名单规则(避免 PHP 处理) # 2. 删除任何将 .js 映射到 fastcgi_pass 的规则 # 3. 重启 Nginx systemctl restart nginx # 4. 测试 JS 文件 curl -I --http1.1 https://szrengjing.com/jsxc/dist/jsxc.bundle.min.js # 5. 再测试 HTTP/2 版本 curl -I https://szrengjing.com/jsxc/dist/jsxc.bundle.min.js ``` 你应该看到两个都能成功返回 `200 OK`。 --- ## 🎉 成功之后 浏览器就能加载 `jsxc.bundle.min.js`,JSXC 应用才能启动。 接下来你可以: - 打开网页测试登录 - 查看浏览器控制台是否有连接 XMPP 服务器的日志 - 如仍失败,再排查 WebSocket 代理 `/xmpp-websocket` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值