Skip to content

在 Nginx 中配置和验证 Brotli 压缩 ​

Brotli 是一种新的压缩算法,提供了比传统 Gzip 更高的压缩率,能有效减小网站资源的体积,从而加速页面加载时间。在 Nginx 中启用 Brotli 压缩,可以显著提高网站性能。本文档提供了如何在 Nginx 中配置 Brotli 压缩以及如何验证 Nginx 是否正确安装了 Brotli 模块的指南。

1. 添加 Brotli 模块到 Nginx ​

由于 Nginx 默认不包含 Brotli 模块,您需要通过编译 Nginx 来添加 Brotli 支持或使用已经包含 Brotli 模块的第三方 Nginx 版本。

使用 Docker 构建包含 Brotli 的 Nginx ​

如果您使用 Docker 来部署 Nginx,可以通过创建自定义 Dockerfile 来构建一个包含 Brotli 模块的 Nginx 镜像。

当然最简单是使用社区构建好的 Docker 镜像 michaelknightdriver/docker-nginx-brotli(推荐)。

创建 Dockerfile ​

Dockerfile
# FROM nginx:1.21.6
# 基于 docker-nginx-brotli 镜像
FROM michaelknightdriver/docker-nginx-brotli

# 删除默认配置文件
RUN rm /etc/nginx/conf.d/default.conf

# 复制自定义的Nginx配置文件
COPY nginx.conf /etc/nginx/conf.d/default.conf

创建 nginx.conf ​

nginx
# upstream 段定义了一组服务器的名称,NGINX 可以将请求代理到该组服务器
upstream loadfrontend {
    # 用于处理 / 路径请求的后端服务器
    server money_backend_frontend:80;
}
upstream loadbackend {
    # 用于处理 /api/ 路径请求的后端服务器
    server money_backend_server:3000;
}

# HTTP服务器配置,用于重定向到HTTPS
server {
    listen 3308; # HTTP监听端口
    server_name docs.ffffee.com; #需要将yourdomain.com替换成证书绑定的域名。
    rewrite ^(.*) https://$server_name:8443$1 permanent; #将所有HTTP请求通过rewrite指令重定向到HTTPS。
}

# server 段定义了对外提供服务的配置
server {
    listen 8443 ssl http2; # HTTPS监听端口
    server_name docs.ffffee.com; # 你的域名

    # SSL证书和密钥位置
    ssl_certificate cert/docs.ffffee.com.pem; # 证书文件路径
    ssl_certificate_key cert/docs.ffffee.com.key; # 私钥文件路径


    # 增强安全性的SSL配置
    # 设置SSL会话的超时时间。5m表示会话超时时间为5分钟。这是一个平衡性能和安全性的设置,较短的超时时间可以减少潜在的会话劫持风险。
    ssl_session_timeout 5m;

    #  定义服务器支持的加密套件。这个列表应该排除已知弱加密和不安全的加密套件。ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4是一个推荐的设置,
    # 它优先使用强加密套件,同时排除了弱加密和易受攻击的加密套件。
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;

    # 指定服务器支持的TLS协议版本。推荐启用TLSv1.2和TLSv1.3,因为它们提供了较强的安全性。
    # TLSv1.1通常不推荐使用,除非是为了向后兼容性考虑,但请注意,随着时间的推移,更多的客户端和服务会弃用TLS 1.1。
    ssl_protocols TLSv1.2 TLSv1.3; # 如果需要向后兼容,则考虑包含TLSv1.1

    # ssl_prefer_server_ciphers: 设置为on时,服务器将按照ssl_ciphers指令中定义的顺序,优先使用服务器端的加密套件选择,而不是客户端提议的顺序。这有助于确保使用最强的加密套件。
    ssl_prefer_server_ciphers on;

    client_max_body_size 10m; # 客户端最大上传大小限制 10MB
    # # 监听端口 3308
    # listen 3308;
    # # 服务器名称,需要替换成证书绑定的域名
    # server_name localhost;

    # location 段定义了处理不同 URL 路径的配置
    location / {
        # Brotli设置
        brotli on;
        brotli_static on;
        brotli_comp_level 6;
        brotli_types text/plain text/css application/javascript application/json image/svg+xml application/xml+rss;

        # 启用gzip_static,优先发送预压缩的.gz文件(如果存在)
        gzip_static on;
        # 为了向后兼容不支持gzip的客户端,保留gzip on
        gzip on;
        # 配置gzip压缩的最小文件大小,避免压缩太小的文件
        gzip_min_length 256;
        # 配置压缩的类型,根据需要进行添加
        gzip_types text/plain application/javascript application/x-javascript text/javascript text/xml text/css application/xml image/svg+xml;

        # 代理请求到 loadfrontend 组的后端服务器
        proxy_pass http://loadfrontend;

        # 设置请求头信息,这些头信息会被转发给后端服务器
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        # 设置 cookie 属性,增强安全性
        proxy_cookie_path / "/; httponly; secure; SameSite=Strict";
    }
    location ^~/api/ {
        # 代理请求到 loadbackend 组的后端服务器
        proxy_pass http://loadbackend;

        # 设置请求头信息,这些头信息会被转发给后端服务器
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        # 设置 cookie 属性,增强安全性
        proxy_cookie_path / "/; httponly; secure; SameSite=Strict";
    }
}

构建和运行容器 ​

bash
docker build -t nginx_with_brotli .
docker run -p 80:80 -p 443:443 nginx_with_brotli

2. 配置 Nginx 以使用 Brotli 压缩 ​

在 Nginx 配置文件中启用 Brotli 压缩。通常,这需要在nginx.conf文件的http块中添加以下配置:

nginx
http {
    brotli on;
    brotli_comp_level 6;
    brotli_static on; # 仅对预先压缩的文件生效
    brotli_types text/plain text/css application/javascript application/json image/svg+xml application/xml+rss;
}

3. 验证 Brotli 模块 ​

方法 1:使用nginx -V ​

在命令行中执行nginx -V,并在输出中查找包含“brotli”的文本,确认 Brotli 模块已经被编译到 Nginx 中。

bash
nginx -V 2>&1 | grep brotli

如果这条命令返回了与 Brotli 相关的配置信息,表示 Brotli 模块已经安装。

方法 2:通过配置文件测试 ​

向nginx.conf添加 Brotli 相关配置,重启 Nginx,并检查错误日志以确认 Brotli 配置没有引发错误。

注意事项 ​

  • 确保您的 Nginx 版本与您尝试使用的 Brotli 模块版本兼容。
  • 测试配置更改前后的网站性能,确保 Brotli 压缩带来的是正面效果。
  • 验证客户端(如浏览器)是否支持 Brotli 压缩,因为不是所有的浏览器都支持此特性。

通过上述步骤,您可以在 Nginx 中成功配置 Brotli 压缩,并验证其正确安装和工作状态。这将有助于提升您网站的加载速度和用户体验。

vite.config.js 配置 ​

js
import { fileURLToPath, URL } from "node:url";
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { visualizer } from "rollup-plugin-visualizer"; // 分析包
import viteCompression from "vite-plugin-compression"; // 生成gzip/brotli压缩的.gz和.br文件插件

// https://vitejs.dev/config/
// eslint-disable-next-line no-unused-vars
export default defineConfig(({ command, mode }) => ({
  plugins: [
    vue(),
    // 使用 compression 插件
    viteCompression({
      verbose: true, // 是否在控制台输出压缩结果
      disable: false, // 默认 false, 设置为 true 来禁用压缩
      threshold: 10240, // 只处理大于此大小的资源(单位:字节)。默认值为 0。
      algorithm: "gzip", // 使用 gzip 压缩
      ext: ".gz", // 输出文件的扩展名
    }),
    viteCompression({
      verbose: true, // 是否在控制台输出压缩结果
      disable: false, // 默认 false, 设置为 true 来禁用压缩
      threshold: 10240, // 只处理大于此大小的资源(单位:字节)。默认值为 0。
      algorithm: "brotliCompress", // 使用 brotli 压缩
      ext: ".br", // 输出文件的扩展名
    }),
    visualizer({
      open: true,
      gzipSize: true,
      brotliSize: true,
      emitFile: true,
      filename: "stats.html",
    }),
  ],
  resolve: {
    alias: {
      "@": fileURLToPath(new URL("./src", import.meta.url)),
    },
  },
  build: {
    rollupOptions: {
      output: {
        manualChunks(id) {
          // 文件路径 id
          console.log(id);

          const chunkArray = [
            "dayjs",
            "@element-plus",
            "vue",
            "vue-router",
            "echarts",
          ];

          if (
            chunkArray.find((chunk) => id.includes(`node_modules/${chunk}/`))
          ) {
            return id
              .toString()
              .split("node_modules/")[1]
              .split("/")[0]
              .toString();
          }
        },
        chunkFileNames: (chunkInfo) => {
          const facadeModuleId = chunkInfo.facadeModuleId
            ? chunkInfo.facadeModuleId.split("/")
            : [];
          const fileName =
            facadeModuleId[facadeModuleId.length - 2] || "[name]";
          return `js/${fileName}/[name].[hash].js`;
        },
      },
    },
  },
  server: {
    proxy: {
      // 当您的前端代码请求 `/api` 路径时,它将被代理到 `http://backend.server.com`
      "/api": "http://localhost:3000",
      // '/api': 'http://ffffee.com:3308'
    },
  },
}));