lolly/docs/28-nginx-api-gateway.md
xfy ae8ea1ce0c docs: 添加 nginx 高级主题文档 (Lua/安全/API网关/动态配置)
新增 5 篇深度文档:
- Lua 模块深度指南:OpenResty、ngx_lua、cosocket
- 安全深度指南:WAF、DDoS 防护、OWASP Top 10
- API 网关配置:路由设计、JWT 验证、限流配额
- 动态配置与服务发现:etcd/Consul、nginx-unit

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-03 16:57:22 +08:00

37 KiB
Raw Permalink Blame History

NGINX API 网关配置指南

1. API 网关概述

什么是 API 网关

API 网关是微服务架构中的关键组件作为单一入口点统一管理和暴露后端服务。NGINX 作为高性能反向代理,非常适合构建功能完善的 API 网关。

API 网关核心功能

功能 说明
请求路由 根据路径、方法、Header 路由到不同后端服务
负载均衡 在多个服务实例间分发请求
认证授权 JWT、OAuth2、API Key 验证
限流熔断 防止过载和服务雪崩
协议转换 HTTP/HTTPS、WebSocket、gRPC 转换
请求/响应转换 修改请求头、响应体、路径重写
缓存加速 缓存常用 API 响应
日志监控 统一收集 API 调用日志和指标

API 网关架构图

                    ┌─────────────────┐
                    │   API Gateway   │
                    │    (NGINX)      │
                    └────────┬────────┘
                             │
           ┌─────────┬───────┴───────┬─────────┐
           │         │               │         │
           ▼         ▼               ▼         ▼
    ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
    │ User Svc │ │ Order Svc│ │PaymentSvc│ │ SearchSvc│
    └──────────┘ └──────────┘ └──────────┘ └──────────┘

2. API 路由设计模式

2.1 基于路径的路由

最常见的路由方式,根据 URL 路径前缀路由到不同服务。

http {
    upstream user_service {
        server user-svc:8080;
    }

    upstream order_service {
        server order-svc:8081;
    }

    upstream payment_service {
        server payment-svc:8082;
    }

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

        # 用户服务路由
        location /api/v1/users/ {
            proxy_pass http://user_service/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }

        # 订单服务路由
        location /api/v1/orders/ {
            proxy_pass http://order_service/;
            proxy_set_header Host $host;
        }

        # 支付服务路由
        location /api/v1/payments/ {
            proxy_pass http://payment_service/;
            proxy_set_header Host $host;
        }
    }
}

2.2 基于请求方法的路由

同一路径根据 HTTP 方法路由到不同服务。

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

    # 查询操作路由到只读服务
    location /api/v1/data/ {
        if ($request_method ~ ^(GET|HEAD)$) {
            proxy_pass http://readonly_backend;
            break;
        }

        # 写入操作路由到主服务
        if ($request_method ~ ^(POST|PUT|PATCH|DELETE)$) {
            proxy_pass http://write_backend;
            break;
        }

        return 405;  # Method Not Allowed
    }
}

2.3 基于 Header 的路由

根据请求头内容路由,常用于 A/B 测试、金丝雀发布。

http {
    map $http_x_api_version $backend_pool {
        default      "stable_backend";
        "v2"         "beta_backend";
        "v2-beta"    "beta_backend";
    }

    upstream stable_backend {
        server api-v1:8080;
    }

    upstream beta_backend {
        server api-v2:8080;
    }

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

        location /api/ {
            proxy_pass http://$backend_pool;
            proxy_set_header Host $host;
        }
    }
}
http {
    map $cookie_app_version $backend_node {
        default     "stable";
        "beta"      "canary";
    }

    upstream stable {
        server api-stable:8080;
    }

    upstream canary {
        server api-canary:8080;
    }

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

        location /api/ {
            proxy_pass http://$backend_node;
        }
    }
}

2.5 流量分流路由(百分比)

http {
    # 使用 split_clients 进行百分比分流
    split_clients "${remote_addr}${http_user_agent}" $variant {
        10%     canary;      # 10% 流量到新版本
        *       stable;      # 90% 流量到稳定版
    }

    upstream stable {
        server api-v1:8080;
    }

    upstream canary {
        server api-v2:8080;
    }

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

        location /api/ {
            proxy_pass http://$variant;
            proxy_set_header X-Variant $variant;
        }
    }
}

2.6 组合路由策略

http {
    # 定义映射变量
    map $http_x_env $target_backend {
        default     prod;
        "staging"   staging;
        "dev"       dev;
    }

    map $http_x_tenant_id $tenant_shard {
        default         shard1;
        ~^1[0-5]        shard1;   # 租户 10-15
        ~^1[6-9]|^2[0]  shard2;   # 租户 16-20
    }

    upstream prod {
        server api-prod-1:8080;
        server api-prod-2:8080;
    }

    upstream staging {
        server api-staging:8080;
    }

    upstream dev {
        server api-dev:8080;
    }

    upstream shard1 {
        server db-shard1:8080;
    }

    upstream shard2 {
        server db-shard2:8080;
    }

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

        # 环境路由
        location /api/ {
            proxy_pass http://$target_backend;
            proxy_set_header X-Tenant-Shard $tenant_shard;

            # 记录路由决策
            access_log /var/log/nginx/api-access.log detailed;
        }

        # 租户数据路由
        location /api/tenant-data/ {
            proxy_pass http://$tenant_shard;
        }
    }
}

3. 请求/响应转换

3.1 请求头转换

添加请求头

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

    location /api/ {
        # 传递客户端信息
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;

        # 添加网关标识
        proxy_set_header X-API-Gateway "nginx";
        proxy_set_header X-Request-ID $request_id;

        # 添加时间戳
        proxy_set_header X-Request-Time $msec;

        proxy_pass http://backend;
    }
}

删除请求头

server {
    location /api/ {
        # 删除敏感请求头,防止信息泄露
        proxy_set_header Authorization "";
        proxy_set_header Cookie "";
        proxy_set_header X-Internal-Token "";

        proxy_pass http://backend;
    }
}

修改请求头

server {
    location /api/ {
        # 重写 Host 头
        proxy_set_header Host backend.internal.com;

        # 基于条件设置
        if ($http_x_client_type = "mobile") {
            proxy_set_header X-Device-Type "mobile";
        }

        proxy_pass http://backend;
    }
}

3.2 响应头转换

添加安全响应头

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

    # 添加 API 响应头
    add_header X-API-Version "v1" always;
    add_header X-RateLimit-Limit "1000" always;

    # 安全头部
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "DENY" always;
    add_header X-XSS-Protection "1; mode=block" always;

    # CORS 头
    add_header Access-Control-Allow-Origin "*" always;
    add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
    add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Request-ID" always;

    location /api/ {
        proxy_pass http://backend;

        # 暴露额外响应头给前端
        expose_headers X-Request-ID X-RateLimit-Remaining;
    }
}

隐藏响应头

server {
    location /api/ {
        proxy_pass http://backend;

        # 隐藏后端服务器信息
        proxy_hide_header X-Powered-By;
        proxy_hide_header X-Runtime;
        proxy_hide_header X-Version;
        proxy_hide_header Server;
    }
}

3.3 请求体转换

请求体大小限制

http {
    # 全局请求体限制
    client_max_body_size 10m;
    client_body_buffer_size 128k;

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

        # 上传接口允许更大请求体
        location /api/v1/upload/ {
            client_max_body_size 100m;
            proxy_pass http://upload_backend;
        }

        # Webhook 接口限制较小
        location /api/v1/webhooks/ {
            client_max_body_size 1m;
            proxy_pass http://webhook_backend;
        }

        # 普通 API
        location /api/ {
            client_max_body_size 5m;
            proxy_pass http://api_backend;
        }
    }
}

3.4 响应体转换sub_filter

修改响应内容

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

    location /api/ {
        proxy_pass http://backend;

        # 替换响应中的内部 URL 为外部 URL
        sub_filter_once off;
        sub_filter_types application/json;

        # 替换后端地址为网关地址
        sub_filter 'http://backend-internal:8080' 'https://api.example.com';

        # 替换版本标识
        sub_filter '"version": "internal"' '"version": "public"';
    }
}

JSON 字段脱敏

server {
    location /api/users/ {
        proxy_pass http://user_backend;

        # 脱敏手机号示例隐藏中间4位
        sub_filter_once off;
        sub_filter_types application/json;
        sub_filter '([0-9]{3})[0-9]{4}([0-9]{4})' '$1****$2';
    }
}

3.5 URL 重写与转换

路径重写

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

    # 旧版本路径兼容
    location /api/v0/ {
        rewrite ^/api/v0/(.*)$ /api/v1/$1 permanent;
    }

    # 内部路径映射
    location /api/public/ {
        rewrite ^/api/public/(.*)$ /internal/api/$1 break;
        proxy_pass http://backend;
    }

    # 带参数重写
    location /api/search/ {
        rewrite ^/api/search/(.*)$ /search?q=$1 break;
        proxy_pass http://search_backend;
    }
}

查询参数处理

server {
    location /api/ {
        # 添加默认参数
        if ($arg_version = "") {
            set $args $args&version=v1;
        }

        # 移除敏感参数
        if ($arg_api_key) {
            set $args '';
            # 或者保留其他参数
            set $args $arg_foo=$arg_foo;
        }

        proxy_pass http://backend;
    }
}

4. JWT 验证

4.1 通过 auth_request 进行 JWT 验证

使用外部认证服务验证 JWT Token。

http {
    upstream api_backend {
        server api:8080;
    }

    upstream auth_service {
        server auth:8080;
    }

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

        location /api/ {
            # JWT 验证
            auth_request /auth_verify;

            # 从认证响应中提取用户信息
            auth_request_set $auth_user $upstream_http_x_user_id;
            auth_request_set $auth_role $upstream_http_x_user_role;
            auth_request_set $auth_tenant $upstream_http_x_tenant_id;

            # 传递给后端
            proxy_set_header X-User-ID $auth_user;
            proxy_set_header X-User-Role $auth_role;
            proxy_set_header X-Tenant-ID $auth_tenant;

            proxy_pass http://api_backend;
        }

        # JWT 验证子请求
        location = /auth_verify {
            internal;
            proxy_pass http://auth_service/verify;
            proxy_pass_request_body off;
            proxy_set_header Content-Length "";

            # 传递 Authorization 头
            proxy_set_header Authorization $http_authorization;
            proxy_set_header X-Original-URI $request_uri;
            proxy_set_header X-Client-IP $remote_addr;

            proxy_connect_timeout 3s;
            proxy_read_timeout 3s;
        }

        # 认证失败处理
        error_page 401 = @unauthorized;
        location @unauthorized {
            default_type application/json;
            return 401 '{"error":"Unauthorized","code":"INVALID_TOKEN"}';
        }

        error_page 403 = @forbidden;
        location @forbidden {
            default_type application/json;
            return 403 '{"error":"Forbidden","code":"INSUFFICIENT_PERMISSIONS"}';
        }
    }
}

4.2 使用 Lua 进行 JWT 验证OpenResty

http {
    lua_package_path "/usr/local/lib/lua/?.lua;;";

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

        location /api/ {
            access_by_lua_block {
                local jwt = require "resty.jwt"
                local validators = require "resty.jwt-validators"

                -- 获取 token
                local auth_header = ngx.var.http_authorization
                if not auth_header then
                    ngx.status = 401
                    ngx.say('{"error":"Missing Authorization header"}')
                    ngx.exit(ngx.HTTP_UNAUTHORIZED)
                end

                local token = string.gsub(auth_header, "Bearer ", "")

                -- 验证 JWT
                local jwt_obj = jwt:verify(
                    ngx.var.jwt_secret,  -- JWT 密钥
                    token,
                    {
                        iss = "https://auth.example.com",
                        validators = {
                            exp = validators.opt_is_not_expired(),
                            iat = validators.opt_is_not_before_now(),
                        }
                    }
                )

                if not jwt_obj.verified then
                    ngx.status = 401
                    ngx.say('{"error":"Invalid token","details":"' .. jwt_obj.reason .. '"}')
                    ngx.exit(ngx.HTTP_UNAUTHORIZED)
                end

                -- 设置变量供后续使用
                ngx.var.jwt_sub = jwt_obj.payload.sub
                ngx.var.jwt_role = jwt_obj.payload.role or "user"
            }

            proxy_set_header X-User-ID $jwt_sub;
            proxy_set_header X-User-Role $jwt_role;
            proxy_pass http://api_backend;
        }
    }
}

4.3 使用 NJSNGINX JavaScript进行 JWT 验证

load_module modules/ngx_http_js_module.so;

http {
    js_import /etc/nginx/jwt_verify.js;

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

        location /api/ {
            # 调用 NJS 验证
            js_set $jwt_payload jwt_verify.verify;

            # 验证失败时拒绝
            if ($jwt_payload = "") {
                return 401 '{"error":"Unauthorized"}';
            }

            proxy_set_header X-JWT-Payload $jwt_payload;
            proxy_pass http://api_backend;
        }
    }
}

jwt_verify.js:

function verify(r) {
    var auth = r.headersIn['Authorization'];
    if (!auth || !auth.startsWith('Bearer ')) {
        return '';
    }

    var token = auth.substring(7);

    try {
        // 简单 JWT 解析base64 解码 payload
        var parts = token.split('.');
        if (parts.length !== 3) {
            return '';
        }

        // 解码 payload
        var payload = parts[1];
        // base64url 解码
        var decoded = Buffer.from(payload, 'base64url').toString();
        var claims = JSON.parse(decoded);

        // 验证过期时间
        if (claims.exp && claims.exp < Math.floor(Date.now() / 1000)) {
            return '';
        }

        return JSON.stringify(claims);
    } catch (e) {
        return '';
    }
}

export default {verify};

4.4 JWT 验证配置参考表

验证方式 优点 缺点 适用场景
auth_request 灵活、业务逻辑外置、支持复杂验证 额外网络请求、延迟增加 需要与认证中心实时交互
Lua (OpenResty) 高性能、功能丰富、社区成熟 需要 OpenResty 或 lua-nginx-module 复杂验证逻辑、本地处理
NJS NGINX 官方支持、无需额外模块 功能相对简单、性能略低 简单验证、官方生态优先

5. 限流与配额管理

5.1 基础限流配置

http {
    # 按 IP 限流区域
    limit_req_zone $binary_remote_addr zone=ip_limit:10m rate=10r/s;

    # 按用户限流区域
    limit_req_zone $http_x_user_id zone=user_limit:10m rate=100r/m;

    # 全局 API 限流
    limit_req_zone $server_name zone=api_global:10m rate=1000r/s;

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

        # 全局限流
        limit_req zone=api_global burst=200 nodelay;

        location /api/ {
            # IP 级限流
            limit_req zone=ip_limit burst=20 nodelay;

            proxy_pass http://api_backend;
        }

        location /api/v1/users/ {
            # 用户级限流
            limit_req zone=user_limit burst=10 nodelay;

            proxy_pass http://user_backend;
        }
    }
}

5.2 分层限流策略

http {
    # 不同层级的限流区域
    limit_req_zone $binary_remote_addr zone=ip:10m rate=30r/s;
    limit_req_zone $http_x_api_key zone=api_key:10m rate=100r/s;
    limit_req_zone $http_x_user_id zone=user:10m rate=60r/m;
    limit_req_zone $server_name zone=global:10m rate=5000r/s;

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

        # 全局保护
        limit_req zone=global burst=1000 nodelay;

        # 健康检查端点不限流
        location /health {
            limit_req off;
            access_log off;
            return 200 '{"status":"ok"}';
        }

        # 公开 API - 仅 IP 限流
        location /api/public/ {
            limit_req zone=ip burst=50 nodelay;
            proxy_pass http://public_backend;
        }

        # 认证 API - IP + API Key 双重限流
        location /api/v1/ {
            limit_req zone=ip burst=30 nodelay;
            limit_req zone=api_key burst=100 nodelay;
            proxy_pass http://api_backend;
        }

        # 用户级 API - 三层限流
        location /api/v1/user/ {
            limit_req zone=ip burst=20 nodelay;
            limit_req zone=api_key burst=50 nodelay;
            limit_req zone=user burst=10 nodelay;
            proxy_pass http://user_backend;
        }
    }
}

5.3 配额管理(基于连接数)

http {
    # 按 API Key 限制并发连接
    limit_conn_zone $http_x_api_key zone=conn_by_key:10m;

    # 按用户限制并发连接
    limit_conn_zone $http_x_user_id zone=conn_by_user:10m;

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

        location /api/v1/stream/ {
            # 每个 API Key 最多 10 个并发连接
            limit_conn conn_by_key 10;

            # 每个用户最多 5 个并发连接
            limit_conn conn_by_user 5;

            proxy_pass http://streaming_backend;
            proxy_buffering off;
        }
    }
}

5.4 限流响应自定义

http {
    limit_req_zone $binary_remote_addr zone=limit:10m rate=10r/s;

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

        location /api/ {
            limit_req zone=limit burst=20 nodelay;

            # 自定义限流响应
            limit_req_status 429;  # Too Many Requests

            proxy_pass http://api_backend;
        }

        # 自定义 429 响应
        error_page 429 @rate_limited;
        location @rate_limited {
            default_type application/json;
            add_header Retry-After 60 always;
            return 429 '{"error":"Rate limit exceeded","retry_after":60,"limit":10}';
        }
    }
}

5.5 基于路径的差异化限流

http {
    # 不同限流区域
    limit_req_zone $binary_remote_addr zone=light:10m rate=100r/m;
    limit_req_zone $binary_remote_addr zone=medium:10m rate=10r/s;
    limit_req_zone $binary_remote_addr zone=heavy:10m rate=1r/s;

    map $uri $rate_limit_zone {
        default          "";
        ~*^/api/health    "none";
        ~*^/api/search    "heavy";
        ~*^/api/reports   "heavy";
        ~*^/api/export    "heavy";
        ~*^/api/webhooks  "light";
    }

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

        location /api/ {
            # 根据路径应用不同限流
            if ($rate_limit_zone = "none") {
                limit_req off;
            }

            if ($rate_limit_zone = "light") {
                limit_req zone=light burst=5 nodelay;
            }

            if ($rate_limit_zone = "heavy") {
                limit_req zone=heavy burst=3 nodelay;
            }

            proxy_pass http://api_backend;
        }
    }
}

5.6 限流指标暴露

http {
    limit_req_zone $binary_remote_addr zone=api:10m rate=100r/s;

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

        location /api/ {
            limit_req zone=api burst=200 nodelay;

            # 添加限流相关响应头
            add_header X-RateLimit-Limit 100 always;
            add_header X-RateLimit-Window 1s always;

            proxy_pass http://api_backend;
        }

        # 限流状态端点
        location /api/status/ratelimit {
            limit_req off;
            stub_status on;
            access_log off;
        }
    }
}

6. API 版本控制策略

6.1 URL 路径版本控制

http {
    upstream api_v1 {
        server api-v1:8080;
    }

    upstream api_v2 {
        server api-v2:8080;
    }

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

        # v1 路由
        location /api/v1/ {
            proxy_pass http://api_v1/;
            proxy_set_header X-API-Version "v1";
        }

        # v2 路由
        location /api/v2/ {
            proxy_pass http://api_v2/;
            proxy_set_header X-API-Version "v2";
        }

        # 默认版本(向后兼容)
        location /api/ {
            rewrite ^/api/(.*)$ /api/v2/$1 break;
            proxy_pass http://api_v2;
        }
    }
}

6.2 Header 版本控制

http {
    upstream api_v1 {
        server api-v1:8080;
    }

    upstream api_v2 {
        server api-v2:8080;
    }

    # 根据 Accept-Version 头路由
    map $http_accept_version $api_version {
        default     "v2";
        "1"         "v1";
        "2"         "v2";
        "v1"        "v1";
        "v2"        "v2";
    }

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

        location /api/ {
            # 版本不存在时返回 400
            if ($api_version = "") {
                return 400 '{"error":"Unsupported API version"}';
            }

            proxy_pass http://api_$api_version;
            proxy_set_header X-API-Version $api_version;
        }
    }
}

6.3 内容协商版本控制

http {
    upstream api_v1 {
        server api-v1:8080;
    }

    upstream api_v2 {
        server api-v2:8080;
    }

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

        location /api/users {
            # 检查 Accept 头中的版本媒体类型
            if ($http_accept ~ "application/vnd\.api\.v2\+json") {
                proxy_pass http://api_v2;
                break;
            }

            if ($http_accept ~ "application/vnd\.api\.v1\+json") {
                proxy_pass http://api_v1;
                break;
            }

            # 默认版本
            proxy_pass http://api_v2;
        }
    }
}

6.4 版本弃用与 Sunset

http {
    server {
        listen 80;
        server_name api.example.com;

        location /api/v1/ {
            # 添加弃用警告头
            add_header Deprecation "true" always;
            add_header Sunset "Sun, 31 Dec 2024 23:59:59 GMT" always;
            add_header Link '</api/v2/>; rel="successor-version"' always;

            proxy_pass http://api_v1;
        }
    }
}

7. OpenAPI/Swagger 集成

7.1 Swagger UI 托管

http {
    server {
        listen 80;
        server_name api.example.com;

        # Swagger UI 静态文件
        location /docs/ {
            alias /var/www/swagger-ui/;
            try_files $uri $uri/ /docs/index.html;
        }

        # OpenAPI 规范文件
        location /api-docs/ {
            alias /etc/nginx/api-docs/;
            default_type application/json;

            # CORS
            add_header Access-Control-Allow-Origin "*" always;
            add_header Access-Control-Allow-Methods "GET, OPTIONS" always;
        }

        # 特定服务文档
        location /api-docs/users.yaml {
            alias /etc/nginx/api-docs/users.yaml;
            default_type text/yaml;
        }

        location /api-docs/orders.yaml {
            alias /etc/nginx/api-docs/orders.yaml;
            default_type text/yaml;
        }
    }
}

7.2 多服务 API 文档聚合

http {
    server {
        listen 80;
        server_name api.example.com;

        # 聚合文档入口
        location /api-docs {
            default_type application/json;
            return 200 '{
                "openapi": "3.0.0",
                "info": {
                    "title": "API Gateway",
                    "version": "1.0.0"
                },
                "servers": [
                    {"url": "https://api.example.com"}
                ],
                "paths": {
                    "/api/v1/users": {"$ref": "/api-docs/users.yaml#/paths/~1users"},
                    "/api/v1/orders": {"$ref": "/api-docs/orders.yaml#/paths/~1orders"}
                }
            }';
        }

        # 代理到各服务文档
        location /api-docs/users/ {
            proxy_pass http://user_service/docs/;
        }

        location /api-docs/orders/ {
            proxy_pass http://order_service/docs/;
        }
    }
}

7.3 API 文档访问控制

http {
    server {
        listen 80;
        server_name api.example.com;

        # 公开文档
        location /docs/public/ {
            alias /var/www/docs/public/;
        }

        # 内部文档(需认证)
        location /docs/internal/ {
            auth_basic "Internal API Docs";
            auth_basic_user_file /etc/nginx/.htpasswd-docs;

            alias /var/www/docs/internal/;
        }

        # API 定义文件保护
        location ~ ^/api-docs/.*\.(yaml|json)$ {
            # 只允许特定 referer
            valid_referers server_names *.example.com;

            if ($invalid_referer) {
                return 403;
            }

            alias /etc/nginx/api-docs/;
        }
    }
}

8. 完整 API 网关配置示例

8.1 生产级 API 网关配置

# /etc/nginx/nginx.conf

user nginx;
worker_processes auto;
worker_rlimit_nofile 65535;

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 4096;
    use epoll;
    multi_accept on;
}

http {
    include /etc/nginx/mime.types;
    default_type application/json;

    # 日志格式
    log_format api_log '$remote_addr - $remote_user [$time_local] '
                       '"$request" $status $body_bytes_sent '
                       '"$http_referer" "$http_user_agent" '
                       'rt=$request_time uct="$upstream_connect_time" '
                       'uht="$upstream_header_time" urt="$upstream_response_time" '
                       'req_id="$request_id" api_key="$http_x_api_key" '
                       'user_id="$jwt_sub" tenant="$jwt_tenant"';

    access_log /var/log/nginx/access.log api_log;

    # 性能优化
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    # 客户端限制
    client_max_body_size 10m;
    client_body_buffer_size 128k;
    client_header_buffer_size 4k;
    large_client_header_buffers 4 8k;

    # 限流区域
    limit_req_zone $binary_remote_addr zone=ip:10m rate=30r/s;
    limit_req_zone $http_x_api_key zone=api_key:10m rate=100r/s;
    limit_req_zone $server_name zone=global:10m rate=10000r/s;

    # 连接限制
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    limit_conn_zone $http_x_api_key zone=api_conn:10m;

    # 上游服务定义
    upstream user_service {
        zone user_service 64k;
        least_conn;
        server user-svc-1:8080 weight=5;
        server user-svc-2:8080 weight=5;
        server user-svc-3:8080 backup;
        keepalive 32;
    }

    upstream order_service {
        zone order_service 64k;
        least_conn;
        server order-svc-1:8080;
        server order-svc-2:8080;
        keepalive 32;
    }

    upstream payment_service {
        zone payment_service 64k;
        server payment-svc-1:8080 max_fails=3 fail_timeout=30s;
        server payment-svc-2:8080 max_fails=3 fail_timeout=30s;
        keepalive 16;
    }

    upstream auth_service {
        zone auth_service 64k;
        server auth-svc:8080;
        keepalive 16;
    }

    # 变量映射
    map $http_x_api_version $api_version {
        default "v1";
        "v1"    "v1";
        "v2"    "v2";
    }

    map $uri $rate_limit_tier {
        default          "medium";
        ~*^/api/health    "none";
        ~*^/api/metrics   "none";
        ~*^/api/export    "strict";
        ~*^/api/search    "strict";
        ~*^/api/webhooks  "relaxed";
    }

    # API Gateway 服务器
    server {
        listen 80;
        listen [::]:80;
        server_name api.example.com;

        # 返回 444 关闭非指定域名访问
        location / {
            return 444;
        }

        # 健康检查(无认证、无限流)
        location = /health {
            access_log off;
            limit_req off;
            return 200 '{"status":"healthy","gateway":"nginx"}';
        }

        # Prometheus 指标端点
        location = /metrics {
            access_log off;
            limit_req off;
            stub_status on;
        }

        # API 文档
        location /docs/ {
            alias /var/www/api-docs/;
            try_files $uri $uri/ =404;

            # 缓存文档
            expires 1h;
            add_header Cache-Control "public, immutable";
        }

        # 主 API 入口
        location /api/ {
            # 请求 ID 生成
            add_header X-Request-ID $request_id always;

            # 全局限流
            limit_req zone=global burst=2000 nodelay;

            # 分层限流
            limit_req zone=ip burst=50 nodelay;
            limit_req zone=api_key burst=100 nodelay;

            # 连接限制
            limit_conn addr 50;
            limit_conn api_conn 100;

            # JWT 验证(可选,根据路径)
            location ~ ^/api/(v1|v2)/(users|orders|payments)/ {
                auth_request /auth/verify;
                auth_request_set $jwt_sub $upstream_http_x_user_id;
                auth_request_set $jwt_role $upstream_http_x_user_role;
                auth_request_set $jwt_tenant $upstream_http_x_tenant_id;

                # 认证失败处理
                error_page 401 = @auth_error;
                error_page 403 = @forbidden_error;

                # 子路径路由
                location ~ ^/api/(v1|v2)/users/ {
                    rewrite ^/api/(v1|v2)/(.*)$ /$2 break;
                    proxy_pass http://user_service;
                }

                location ~ ^/api/(v1|v2)/orders/ {
                    rewrite ^/api/(v1|v2)/(.*)$ /$2 break;
                    proxy_pass http://order_service;
                }

                location ~ ^/api/(v1|v2)/payments/ {
                    rewrite ^/api/(v1|v2)/(.*)$ /$2 break;
                    proxy_pass http://payment_service;
                }
            }

            # 公开 API无需认证
            location ~ ^/api/(v1|v2)/public/ {
                rewrite ^/api/(v1|v2)/(.*)$ /$2 break;
                proxy_pass http://public_service;
            }
        }

        # 认证子请求
        location = /auth/verify {
            internal;
            proxy_pass http://auth_service/verify;
            proxy_pass_request_body off;
            proxy_set_header Content-Length "";
            proxy_set_header Authorization $http_authorization;
            proxy_set_header X-Original-URI $request_uri;
            proxy_set_header X-Original-Method $request_method;
            proxy_connect_timeout 3s;
            proxy_read_timeout 3s;
        }

        # WebSocket 支持
        location /ws/ {
            proxy_pass http://websocket_backend;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_read_timeout 86400s;
            proxy_send_timeout 86400s;
        }

        # 错误处理
        error_page 500 502 503 504 @api_error;
        location @api_error {
            internal;
            default_type application/json;
            add_header X-Error-Source "gateway" always;
            return 500 '{"error":"Internal Server Error","code":"GATEWAY_ERROR"}';
        }

        location @auth_error {
            internal;
            default_type application/json;
            return 401 '{"error":"Unauthorized","code":"AUTH_REQUIRED"}';
        }

        location @forbidden_error {
            internal;
            default_type application/json;
            return 403 '{"error":"Forbidden","code":"ACCESS_DENIED"}';
        }

        location @rate_limited {
            internal;
            default_type application/json;
            add_header Retry-After 60 always;
            return 429 '{"error":"Rate limit exceeded","code":"RATE_LIMITED","retry_after":60}';
        }
    }

    # HTTPS 服务器
    server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        server_name api.example.com;

        # SSL 配置
        ssl_certificate /etc/nginx/ssl/api.example.com.crt;
        ssl_certificate_key /etc/nginx/ssl/api.example.com.key;
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
        ssl_prefer_server_ciphers off;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 1d;
        ssl_session_tickets off;

        # HSTS
        add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

        # 安全头部
        add_header X-Frame-Options "DENY" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header X-XSS-Protection "1; mode=block" always;
        add_header Referrer-Policy "strict-origin-when-cross-origin" always;

        # 重用 HTTP 配置
        include /etc/nginx/api-locations.conf;
    }
}

8.2 配置结构建议

/etc/nginx/
├── nginx.conf              # 主配置
├── conf.d/
│   ├── 00-upstreams.conf   # 上游服务定义
│   ├── 10-limits.conf      # 限流配置
│   ├── 20-maps.conf        # 变量映射
│   └── 30-ssl.conf         # SSL 通用配置
├── sites-enabled/
│   └── api-gateway.conf    # API 网关服务器配置
├── api-docs/               # API 文档
│   ├── openapi.yaml
│   ├── users.yaml
│   └── orders.yaml
└── ssl/
    ├── api.example.com.crt
    └── api.example.com.key

8.3 常用指令速查表

类别 指令 说明
路由 proxy_pass 代理到后端
rewrite URL 重写
map 变量映射
请求头 proxy_set_header 设置代理请求头
proxy_hide_header 隐藏响应头
add_header 添加响应头
认证 auth_request 外部认证
auth_request_set 提取认证变量
限流 limit_req_zone 定义限流区域
limit_req 应用限流
limit_conn_zone 定义连接限制区域
limit_conn 应用连接限制
转换 sub_filter 响应内容替换
client_max_body_size 请求体大小限制

9. 最佳实践

9.1 安全最佳实践

  1. 始终使用 HTTPS:生产环境强制 TLSv1.2+
  2. 隐藏后端信息:移除 Server、X-Powered-By 等响应头
  3. 启用 HSTS:防止降级攻击
  4. 实施认证:敏感接口必须认证
  5. 输入验证:限制请求体大小和方法

9.2 性能最佳实践

  1. 启用 keepalive:减少连接建立开销
  2. 合理配置缓冲区:平衡内存使用和响应速度
  3. 使用缓存:对读多写少的 API 启用缓存
  4. 连接池:配置上游 keepalive 连接
  5. 启用 gzip:压缩 JSON 响应

9.3 可观测性最佳实践

  1. 结构化日志:包含请求 ID、用户 ID、耗时等
  2. 健康检查端点:便于负载均衡器探测
  3. 指标暴露:使用 stub_status 或 nginx-module-vts
  4. 分布式追踪:传递 Trace ID 到后端
  5. 告警配置:对错误率和延迟设置告警

9.4 部署检查清单

  • 配置文件语法验证 (nginx -t)
  • 限流阈值合理性验证
  • SSL 证书有效性检查
  • 后端服务连通性测试
  • 认证流程端到端测试
  • 错误响应格式验证
  • 性能基准测试
  • 安全扫描端口、Header 等)