3.6 KiB
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 JSONproxy_buffering off— required for SSE streamingproxy_socket_keepalive on— reduce TIME_WAIT on localhosthttp2 on;(notlisten ... http2) — nginx 1.28 deprecationproxy_intercept_errors on— enables error_page for upstream errorsproxy_set_header Connection ""— prevents hop-by-hop forwarding