4

I want to use nginx as a https frontend and I'm looking for a way to limit TCP connections. These limits should be based on the remote IP addresses and work for HTTP/1.1 as well as HTTP 2.

For HTTP/1.1 I thought I would be fine around 15 concurrent connections per IP (since most browsers seems to use at most 6), wich should be more than enough for HTTP/2. But the nginx docs states:

In HTTP/2 and SPDY, each concurrent request is considered a separate connection.

And the HTTP/2 spec states about the SETTINGS_MAX_CONCURRENT_STREAMS parameter:

It is recommended that this value be no smaller than 100, so as to not unnecessarily limit parallelism.

Does this mean that I should allow at least 100 connections per remote IP? It seems so in my tests, but maybe I'm missing something. Because this seams to essentially mean that I can't set any meaningful limits for the number of concurrent TCP connections, without seriously limiting HTTP/2.

8
  • IMHO, this depends on your architecture and your work load. What do you want to achieve? What problem(s) do you try to solve? I'm flagging this as "too broad" for now.
    – gxx
    Commented Sep 11, 2017 at 14:14
  • I want to enforce a limit of 15 concurrent connections per IP and a request rate of 1–3 req/s (with a burst value around 150). I'm unsure on how to write this because I'm unsure what qualifies as request and what as connections in HTTP/2 vs HTTP1. I'm not sold on the numbers, they will be tweaked later…
    – Brutus
    Commented Sep 11, 2017 at 14:27
  • Seems a valid question to me - that nginx documentation is a bit confusing. It seems to say this will treat each stream as a connection, which would make it difficult to set a sensible limit for both HTTP/1 and HTTP/2 clients. Seems a bit silly to be honest. Should be easy enough to test it though. Set the limit to 1, try to connect from a browser (that supports HTTP/2) and see if that rate limits it to one connection stream. Commented Sep 11, 2017 at 18:39
  • You could set different limits for HTTP <2 and HTTP =2, depending on the protocol, leveraging the map module. Regarding your main question: Just test and see how it goes? I still don't understand how we could help with that, or better, you're able to help yourself, IMHO.
    – gxx
    Commented Sep 12, 2017 at 8:19
  • Yes, I do test this for myself and to no big surprise, the documentation seems right. I was hoping I'm missing something ;) This behavior seems very counterintuitive to me.
    – Brutus
    Commented Sep 13, 2017 at 7:46

2 Answers 2

3

Here's how I solved it for now:

map $http2 $v1ip {
    default "";
    ""  $binary_remote_addr;
}
map $http2 $v2ip {
    default $binary_remote_addr;
    "" "";
}

limit_conn_zone $v1ip zone=v1ips:10m;
limit_conn v1ips 10;

limit_conn_zone $v2ip zone=v2ips:10m;
limit_conn v2ips 125;

Since the connection limit for HTTP/2 has to be that high, I also added rate limiting… but I'm still tweaking the values.

NOTE: while this allows setting a "sensible" value for HTTP < 2 connections, it still requires an — imo unnecessary — high number of concurrent connections for HTTP/2 connections. I would very much like to see limit_conn to only care for TCP connections and not HTTP requests.

2
  • Really neat. One of the gems in ServerFault.com. Thanks!
    – Basil A
    Commented Mar 25, 2019 at 13:43
  • That allows 135 connections in total per IP address. Ditch $v2ip, and create two limits key=$v1ip limit=10 and key=$binary_remote_addr limit=125. Commented Jun 2, 2021 at 5:55
0

The above solution is pretty good, if with whitelist I think using "" is not good.

should be use $request_id to replace "" be better

geo $whitelist {
      default 0;
      127.0.0.1/32 1;
}

map $http2 $http1_ip {
      default $request_id;
      "" $binary_remote_addr;

  }

map $http2 $http2_ip {
      default $binary_remote_addr;
      "" $request_id;

  }

map $whitelist $http1_ip_final {
      default $http1_ip;
      1 $request_id;

}

map $whitelist $http2_ip_final {
      default $http2_ip;
      1 $request_id;

}

    limit_conn_zone                 $http1_ip_final zone=conn_limit_per_ip_http1:10m;
    limit_conn                      conn_limit_per_ip_http1 10;
    limit_conn_zone                 $http2_ip_final zone=conn_limit_per_ip_http2:10m;
    limit_conn                      conn_limit_per_ip_http2 125;

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .