Files
autoreg-skills-max/skills/reseller-api-gateway/references/nginx-config.md

3.6 KiB

Nginx Config — API Gateway (HTTP/2 + Gzip + Error Masking)

Production config for api.<domain> on nginx 1.28.3.

Server block (SSL + HTTP/2)

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name api.[REDACTED-NAME].loc.cc;
    server_tokens off;

    ssl_certificate /etc/letsencrypt/live/[REDACTED-NAME].loc.cc/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/[REDACTED-NAME].loc.cc/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    client_max_body_size 25m;
    gzip on;
    gzip_min_length 256;
    gzip_types application/json text/event-stream;
    gzip_comp_level 1;
    gzip_vary on;

    location / {
        proxy_pass http://[REDACTED-NAME];
        proxy_http_version 1.1;
        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-Forwarded-Proto $scheme;
        proxy_set_header X-Request-ID $request_id;
        proxy_set_header Connection "";
        proxy_connect_timeout 10s;
        proxy_read_timeout 600s;
        proxy_send_timeout 600s;
        proxy_request_buffering off;
        proxy_buffering off;
        proxy_cache off;
        proxy_socket_keepalive on;
        proxy_intercept_errors on;
    }

    # Error masking: ALL upstream/nginx errors → neutral JSON
    error_page 400 408 411 414 431 = @invalid_request;
    error_page 405 = @method_not_allowed;
    error_page 413 = @request_too_large;
    error_page 500 502 503 504 = @service_unavailable;

    location @invalid_request {
        internal;
        default_type application/json;
        return 400 '{"error":{"message":"Invalid request","type":"gateway_error","code":"invalid_request"}}';
    }

    location @method_not_allowed {
        internal;
        default_type application/json;
        return 405 '{"error":{"message":"Method not allowed","type":"gateway_error","code":"method_not_allowed"}}';
    }

    location @request_too_large {
        internal;
        default_type application/json;
        return 413 '{"error":{"message":"Request body is too large","type":"gateway_error","code":"request_too_large"}}';
    }

    location @service_unavailable {
        internal;
        default_type application/json;
        return 502 '{"error":{"message":"Service temporarily unavailable","type":"gateway_error","code":"service_unavailable"}}';
    }
}

Upstream block

upstream [REDACTED-NAME] {
    zone [REDACTED-NAME] 64k;
    server 127.0.0.1:18181;
    keepalive 64;
}

HTTP → HTTPS redirect

server {
    listen 80;
    listen [::]:80;
    server_name [REDACTED-NAME].loc.cc www.[REDACTED-NAME].loc.cc api.[REDACTED-NAME].loc.cc;
    return 301 https://$host$request_uri;
}

Echo/legacy paths (block on old domain)

server {
    listen 443 ssl;
    server_name [REDACTED-NAME].loc.cc www.[REDACTED-NAME].loc.cc;
    # ... SSL ...
    location ^~ /echo/ {
        default_type application/json;
        return 404 '{"error":{"message":"Not found","type":"gateway_error","code":"not_found"}}';
    }
    location / {
        proxy_pass http://127.0.0.1:18080;
        # ... proxy ...
    }
}

Key decisions

  • gzip_comp_level 1 — minimal CPU cost, sufficient compression for JSON
  • proxy_buffering off — required for SSE streaming
  • proxy_socket_keepalive on — reduce TIME_WAIT on localhost
  • http2 on; (not listen ... http2) — nginx 1.28 deprecation
  • proxy_intercept_errors on — enables error_page for upstream errors
  • proxy_set_header Connection "" — prevents hop-by-hop forwarding