Nginx 压缩与静态文件优化

Compression and Static File Optimization

概述

压缩和静态文件优化是提升Web性能的关键技术。Nginx提供了强大的压缩功能和静态文件处理能力,能够显著减少带宽使用和提升加载速度。

1. Gzip压缩配置

1.1 基础Gzip配置

http {
    # 启用gzip压缩
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/javascript
        application/xml+rss
        application/atom+xml
        image/svg+xml
        application/json
        application/ld+json;

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

        location / {
            try_files $uri $uri/ =404;
        }
    }
}

1.2 高级Gzip配置

http {
    # 详细的gzip配置
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_proxied expired no-cache no-store private auth;

    # 排除已压缩文件
    gzip_disable "msie6";

    # 扩展的MIME类型
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        text/x-component
        application/javascript
        application/x-javascript
        application/json
        application/xml
        application/rss+xml
        application/atom+xml
        font/truetype
        font/opentype
        application/vnd.ms-fontobject
        image/svg+xml;

    server {
        listen 80;
        server_name advanced-gzip.example.com;

        location / {
            # 根据文件大小调整压缩级别
            location ~* \.(js|css)$ {
                gzip_comp_level 9;  # 高压缩比
                expires 1y;
            }

            location ~* \.(html|htm)$ {
                gzip_comp_level 6;  # 平衡压缩比
                expires 1h;
            }

            try_files $uri $uri/ =404;
        }
    }
}

2. Brotli压缩

2.1 Brotli配置

# 需要安装nginx-module-brotli模块
load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;

http {
    # Brotli压缩配置
    brotli on;
    brotli_comp_level 6;
    brotli_min_length 1024;
    brotli_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/javascript
        application/json
        application/xml
        application/rss+xml
        application/atom+xml
        image/svg+xml;

    # 静态Brotli文件
    brotli_static on;

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

        location ~* \.(js|css|html)$ {
            # 优先使用预压缩的brotli文件
            try_files $uri.br $uri =404;
            add_header Content-Encoding br;
            add_header Vary Accept-Encoding;
        }
    }
}

2.2 多重压缩策略

http {
    # 同时启用gzip和brotli
    gzip on;
    gzip_comp_level 6;
    gzip_types text/plain text/css application/javascript;

    brotli on;
    brotli_comp_level 6;
    brotli_types text/plain text/css application/javascript;

    server {
        listen 80;
        server_name multi-compress.example.com;

        location ~* \.(js|css)$ {
            # 根据客户端支持选择压缩方式
            try_files $uri.br $uri.gz $uri =404;

            location ~* \.br$ {
                add_header Content-Encoding br;
                add_header Content-Type application/javascript;
            }

            location ~* \.gz$ {
                add_header Content-Encoding gzip;
                add_header Content-Type application/javascript;
            }
        }
    }
}

3. 静态文件优化

3.1 文件服务优化

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

    # 开启sendfile
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    # 文件传输优化
    location ~* \.(jpg|jpeg|png|gif|ico|svg)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        add_header Vary Accept-Encoding;

        # 优化大文件传输
        location ~* \.(pdf|zip|mp4|mp3)$ {
            # 启用分片传输
            slice 1m;
            proxy_cache_key $host$uri$is_args$args$slice_range;
            proxy_set_header Range $slice_range;
            proxy_cache_valid 200 206 1h;
        }

        # 关闭访问日志
        access_log off;
    }

    # CSS和JS优化
    location ~* \.(css|js)$ {
        expires 30d;
        add_header Cache-Control "public";

        # 启用gzip_static
        gzip_static on;

        # ETag支持
        etag on;
    }
}

3.2 图片优化

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

    # WebP图片支持
    location ~* \.(jpg|jpeg|png)$ {
        # 检查是否支持WebP
        if ($http_accept ~* "webp") {
            rewrite ^(.*)$ $1.webp last;
        }

        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    location ~* \.webp$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        add_header Vary Accept;

        # 如果webp文件不存在,回退到原图
        try_files $uri @fallback;
    }

    location @fallback {
        rewrite ^(.*)\.webp$ $1 last;
    }

    # 响应式图片
    location /responsive/ {
        # 根据屏幕密度提供不同图片
        if ($http_user_agent ~* "Mobile") {
            rewrite ^/responsive/(.+)$ /mobile/$1 last;
        }

        if ($arg_dpr = "2") {
            rewrite ^/responsive/(.+)$ /2x/$1 last;
        }

        try_files $uri =404;
    }
}

4. 文件合并和最小化

4.1 CSS/JS合并

# 使用concat模块进行文件合并
location /combo {
    concat on;
    concat_types text/css application/javascript;
    concat_unique off;
    concat_max_files 30;
    concat_delimiter "\n";

    # 示例: /combo??style1.css,style2.css,style3.css
    root /var/www/assets;
}

# 传统方法:预合并文件
location /assets/ {
    # 尝试合并文件,否则回退到单独文件
    try_files $uri /combo$uri =404;

    expires 1y;
    add_header Cache-Control "public, immutable";
    gzip_static on;
}

4.2 动态资源优化

# 使用substitute模块进行内容替换
location / {
    # 移除HTML中的空白字符
    substitute '\s+' ' ' g;
    substitute '>\s+<' '><' g;
    substitute_filter_types text/html;

    proxy_pass http://backend;
}

# 使用Lua进行动态优化
location /optimize {
    content_by_lua_block {
        local template = ngx.location.capture("/template.html")
        if template.status == 200 then
            -- 移除注释和多余空白
            local content = template.body
            content = string.gsub(content, "<!--.--->", "")
            content = string.gsub(content, "%s+", " ")
            content = string.gsub(content, "> <", "><")

            ngx.header.content_type = "text/html"
            ngx.say(content)
        else
            ngx.status = 500
            ngx.say("Error loading template")
        end
    }
}

5. CDN集成优化

5.1 CDN回源优化

# CDN边缘服务器配置
server {
    listen 80;
    server_name cdn-edge.example.com;

    # 设置源站
    location / {
        proxy_pass http://origin-server.example.com;

        # CDN优化头
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-CDN-Pop $server_name;

        # 缓存控制
        proxy_cache cdn_cache;
        proxy_cache_key "$scheme$proxy_host$request_uri";
        proxy_cache_valid 200 1h;
        proxy_cache_valid 404 1m;

        # 添加CDN头
        add_header X-Cache-Status $upstream_cache_status;
        add_header X-CDN-Pop $server_name;
    }

    # 静态资源长缓存
    location ~* \.(css|js|png|jpg|gif|ico)$ {
        proxy_pass http://origin-server.example.com;
        proxy_cache cdn_cache;
        proxy_cache_valid 200 7d;

        expires 30d;
        add_header Cache-Control "public, immutable";
    }
}

5.2 多CDN负载均衡

upstream cdn_nodes {
    server cdn1.example.com weight=3;
    server cdn2.example.com weight=2;
    server cdn3.example.com weight=1 backup;
}

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

    location / {
        # 根据文件类型选择CDN节点
        if ($request_uri ~* \.(css|js)$) {
            proxy_pass http://cdn_nodes;
            break;
        }

        if ($request_uri ~* \.(png|jpg|gif)$) {
            proxy_pass http://image_cdn;
            break;
        }

        proxy_pass http://default_cdn;
    }
}

6. 性能监控

6.1 压缩效果监控

# 压缩比统计
log_format compression '$remote_addr - $remote_user [$time_local] '
                      '"$request" $status $bytes_sent '
                      '"$http_referer" "$http_user_agent" '
                      'compression_ratio:$gzip_ratio';

server {
    access_log /var/log/nginx/compression.log compression;

    location / {
        gzip on;
        gzip_comp_level 6;
        proxy_pass http://backend;
    }
}

6.2 性能分析脚本

#!/bin/bash
# compression-analyzer.sh

LOG_FILE="/var/log/nginx/access.log"

analyze_compression() {
    echo "=== 压缩效果分析 ==="

    # 分析不同文件类型的压缩比
    echo "文件类型压缩统计:"
    tail -10000 $LOG_FILE | \
    awk '{
        if ($7 ~ /\.css$/) css_count++
        if ($7 ~ /\.js$/) js_count++
        if ($7 ~ /\.html$/) html_count++
        if ($10 != "-") {
            if ($7 ~ /\.css$/) css_size += $10
            if ($7 ~ /\.js$/) js_size += $10
            if ($7 ~ /\.html$/) html_size += $10
        }
    }
    END {
        printf "CSS: %d 请求, 平均大小: %.2f KB\n", css_count, css_size/css_count/1024
        printf "JS: %d 请求, 平均大小: %.2f KB\n", js_count, js_size/js_count/1024
        printf "HTML: %d 请求, 平均大小: %.2f KB\n", html_count, html_size/html_count/1024
    }'
}

check_compression_ratio() {
    echo "=== 压缩比检查 ==="

    # 检查gzip压缩比
    curl -H "Accept-Encoding: gzip" -s -w "压缩后大小: %{size_download} bytes\n" \
         http://localhost/style.css > /dev/null

    curl -s -w "原始大小: %{size_download} bytes\n" \
         http://localhost/style.css > /dev/null
}

monitor_static_files() {
    echo "=== 静态文件性能监控 ==="

    # 统计静态文件请求
    static_requests=$(tail -1000 $LOG_FILE | \
                     grep -E '\.(css|js|png|jpg|gif|ico)' | wc -l)

    total_requests=$(tail -1000 $LOG_FILE | wc -l)
    static_percentage=$((static_requests * 100 / total_requests))

    echo "静态文件请求占比: ${static_percentage}%"

    # 分析缓存命中率
    cache_hits=$(tail -1000 $LOG_FILE | grep -c "X-Cache-Status: HIT")
    cache_rate=$((cache_hits * 100 / total_requests))

    echo "缓存命中率: ${cache_rate}%"
}

case "$1" in
    "compression") analyze_compression ;;
    "ratio") check_compression_ratio ;;
    "static") monitor_static_files ;;
    "all") 
        analyze_compression
        echo
        check_compression_ratio
        echo
        monitor_static_files
        ;;
    *)
        echo "用法: $0 {compression|ratio|static|all}"
        ;;
esac

7. 自动化优化

7.1 构建时优化

#!/bin/bash
# build-optimizer.sh

ASSETS_DIR="/var/www/assets"
BUILD_DIR="/tmp/build"

# 创建构建目录
mkdir -p $BUILD_DIR

# CSS优化
optimize_css() {
    echo "优化CSS文件..."
    for css_file in $ASSETS_DIR/*.css; do
        filename=$(basename "$css_file")
        # 使用cssnano压缩CSS
        npx cssnano "$css_file" "$BUILD_DIR/$filename"

        # 生成gzip版本
        gzip -c "$BUILD_DIR/$filename" > "$BUILD_DIR/${filename}.gz"

        # 生成brotli版本
        brotli -c "$BUILD_DIR/$filename" > "$BUILD_DIR/${filename}.br"
    done
}

# JavaScript优化
optimize_js() {
    echo "优化JavaScript文件..."
    for js_file in $ASSETS_DIR/*.js; do
        filename=$(basename "$js_file")
        # 使用terser压缩JS
        npx terser "$js_file" -o "$BUILD_DIR/$filename" -c -m

        # 生成压缩版本
        gzip -c "$BUILD_DIR/$filename" > "$BUILD_DIR/${filename}.gz"
        brotli -c "$BUILD_DIR/$filename" > "$BUILD_DIR/${filename}.br"
    done
}

# 图片优化
optimize_images() {
    echo "优化图片文件..."
    for img_file in $ASSETS_DIR/*.{jpg,png}; do
        if [ -f "$img_file" ]; then
            filename=$(basename "$img_file")
            # 使用imagemagick优化
            convert "$img_file" -quality 85 -strip "$BUILD_DIR/$filename"

            # 生成WebP版本
            cwebp -q 85 "$img_file" -o "$BUILD_DIR/${filename%.*}.webp"
        fi
    done
}

# 执行优化
optimize_css
optimize_js
optimize_images

# 部署优化后的文件
rsync -av $BUILD_DIR/ $ASSETS_DIR/
echo "优化完成!"

7.2 监控和告警

#!/bin/bash
# performance-monitor.sh

THRESHOLD_SIZE=1048576  # 1MB
THRESHOLD_RATIO=50      # 50%压缩比

check_file_sizes() {
    large_files=$(find /var/www -name "*.js" -o -name "*.css" -size +${THRESHOLD_SIZE}c)

    if [ -n "$large_files" ]; then
        echo "警告:发现大文件需要优化:"
        echo "$large_files"
        return 1
    fi

    return 0
}

check_compression_config() {
    if ! nginx -T | grep -q "gzip on"; then
        echo "警告:gzip压缩未启用"
        return 1
    fi

    return 0
}

# 主监控循环
while true; do
    if ! check_file_sizes || ! check_compression_config; then
        echo "性能检查失败,需要注意!"
        # 发送告警邮件
        echo "性能告警" | mail -s "Nginx性能告警" admin@example.com
    fi

    sleep 3600  # 每小时检查一次
done

小结

通过本文学习,你应该掌握:

  1. Gzip和Brotli压缩配置
  2. 静态文件优化技巧
  3. 图片和资源优化方法
  4. CDN集成和负载均衡
  5. 性能监控和分析
  6. 自动化优化流程

下一篇文章将介绍Nginx的连接优化与调优。

powered by Gitbook© 2025 编外计划 | 最后修改: 2025-08-29 15:40:15

results matching ""

    No results matching ""