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.

Brutus
  • 171
  • 1
  • 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 Sep 11 '17 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 Sep 11 '17 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. – Barry Pollard Sep 11 '17 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 Sep 12 '17 at 08: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 Sep 13 '17 at 07:46
  • a) "[...] 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.": Well, as written above, you've to use different limits, depending on the protocol the user is using. b) Please ping me via @, otherwise I won't be notified. – gxx Sep 13 '17 at 09:22
  • @gf_ different limits for different HTTP versions seems the way to go. How can I set those? I know the _map_ module but I don't know how to get the HTTP version. – Brutus Sep 13 '17 at 12:19
  • Use `$server_protocol`. – gxx Sep 13 '17 at 13:28

2 Answers2

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.

Brutus
  • 171
  • 1
  • 8
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;
Louis He
  • 1
  • 1