When needing to proxy an entire range of ports (say 9000-9999) to equivalent ports on a backend server, the naive approach would be writing individual server
blocks for each port. This quickly becomes unmanageable:
# Painful manual configuration example - DON'T DO THIS server { listen 9000; proxy_pass backend:9000; } server { listen 9001; proxy_pass backend:9001; } # ...and 999 more blocks!
Nginx's stream module (available since version 1.9.0) supports port ranges in listen directives:
stream { server { listen 9000-9999; proxy_pass backend:$server_port; } }
The magic happens with $server_port
which dynamically captures the incoming port number and reuses it for the backend connection.
Here's a full working configuration with important optimizations:
worker_processes auto; error_log /var/log/nginx/error.log; events { worker_connections 1024; } stream { # TCP/UDP proxy for port range server { listen 9000-9999; proxy_pass backend_server:$server_port; proxy_timeout 60s; proxy_connect_timeout 2s; } # Optional: UDP specific configuration server { listen 9000-9999 udp; proxy_pass backend_server:$server_port; proxy_timeout 60s; proxy_responses 1; } } http { # Regular HTTP configuration # ... (your existing HTTP config) }
When dealing with 1000+ ports:
- Set
worker_rlimit_nofile
to a high value (like 65535) in main context - Increase
worker_connections
in events block - Consider separating TCP and UDP traffic if both are needed
- Monitor
netstat -tuln
to verify all ports are bound
For more complex routing logic, you can use OpenResty's Lua module:
server { listen 9000-9999; access_by_lua_block { ngx.var.backend_port = ngx.var.server_port } proxy_pass http://backend:$backend_port; }
After configuration:
# Check bound ports ss -tulnp | grep nginx # Test connectivity for port in {9000..9005}; do echo "Testing port $port" curl -v http://localhost:$port done
When dealing with large port ranges (e.g., 9000-9999), manually configuring each port in Nginx would be tedious and inefficient. The goal is to proxy all traffic from ports 9000-9999 on the local machine to the same ports on a different IP address without writing thousands of lines of configuration.
The ngx_stream_core_module
(introduced in Nginx 1.9.0) is perfect for this scenario. It allows TCP/UDP proxying without requiring individual server
blocks for each port.
load_module /usr/lib/nginx/modules/ngx_stream_module.so;
Here's how to configure Nginx to handle the entire port range dynamically:
stream {
server {
listen 9000-9999;
proxy_pass backend_server:$server_port;
}
upstream backend_server {
server destination_ip:9000-9999;
}
}
For more complex scenarios where you need additional processing, you can use variables:
stream {
map $remote_port $backend_port {
default $remote_port;
}
server {
listen 9000-9999;
proxy_pass destination_ip:$backend_port;
}
}
When proxying large port ranges:
- Monitor connection limits (
worker_connections
) - Consider using
reuseport
for better performance - Adjust kernel parameters for large numbers of sockets
Remember that:
- All ports in the range will be exposed
- Firewall rules should be configured accordingly
- Consider rate limiting if applicable
After implementing, test with:
nginx -t
systemctl restart nginx
Then verify connectivity to any port in the range (e.g., 9000, 9500, 9999).