Nginx servers on both *:80 and <ip>:80? also duplicate listen parameters error when binding by just

I would like to have an Nginx setup where I have specific logic
depending
on which interface (ip) the request arrived on.

I was able to make this work by having a server stanza for each ip on
the
server, but was’t able to do a combination of a specific ip and a
wildcard
ip (as a catchall) - is there a way to do that with some option
combination
(i.e. nginx listens on *:80, but matches the server stanza by ip?)

The scenario I’m playing towards is that I have a dedicated connection
to a
CDN and I want to pass thru certain headers if they arrive via the
dedicated interface, strip them if they arrive on other interface.

When I did the server{} per IP approach nginx complained about duplicate
listen settings for the second IP even though both server stanzas were
bound to a specific port/interface. Is this a bug per chance?

This was with Nginx 1.9.0 btw, perhaps newer versions have a different
behavior?

On Thu, Mar 31, 2016 at 01:21:02PM -0400, CJ Ess wrote:

Hi there,

I would like to have an Nginx setup where I have specific logic depending
on which interface (ip) the request arrived on.

multiple server{} with different “listen”; possibly with an “include
common-config” entry.

Note: “listen” is on an ip, not an interface.

I was able to make this work by having a server stanza for each ip on the
server, but was’t able to do a combination of a specific ip and a wildcard
ip (as a catchall) - is there a way to do that with some option combination
(i.e. nginx listens on *:80, but matches the server stanza by ip?)

I don’t understand what you are describing. Could you try again, perhaps
with a config example?

When I use

===
server {
listen 127.0.0.1:8088;
return 200 “listen 127.0.0.1:8088\n”;
}
server {
listen 10.0.1.2:8088;
return 200 “listen 10.0.1.2:8088\n”;
}
server {
listen 8088;
return 200 “listen 8088\n”;
}

I get the following output, which is what I expect:

$ curl http://127.0.0.1:8088/
listen 127.0.0.1:8088
$ curl http://127.0.0.2:8088/
listen 8088

The scenario I’m playing towards is that I have a dedicated connection to a
CDN and I want to pass thru certain headers if they arrive via the
dedicated interface, strip them if they arrive on other interface.

As above, if “interface” is replaced with “ip”, this can work with two
server{} blocks.

When I did the server{} per IP approach nginx complained about duplicate
listen settings for the second IP even though both server stanzas were
bound to a specific port/interface. Is this a bug per chance?

What short server{} config can I use to reproduce the complaint?

f

Francis D. [email protected]

Your right, I should make a simple test case like you did in the prev
message. I’ll put that together.

Francis,

Sorry it took so long, I’ve finally come back to this question.

The example you gave works great:

server {
    listen 127.0.0.1:8088;
    return 200 "listen 127.0.0.1:8088\n";
}
server {
    listen 10.0.1.2:8088;
    return 200 "listen 10.0.1.2:8088\n";
}
server {
    listen 8088;
    return 200 "listen 8088\n";
}

Where I get into problem is if I do something like this:

server {
    listen 127.0.0.1:8088 backlog=65536 deferred;
    return 200 "listen 127.0.0.1:8088\n";
}
server {
    listen 10.0.1.2:8088 backlog=65536 deferred;
    return 200 "listen 10.0.1.2:8088\n";
}
server {
    listen 8088;
    return 200 "listen 8088\n";
}

In that case I get errors like “nginx: [emerg] bind() to 0.0.0.0:8088
failed (98: Address already in use)”. So the workaround is obviously not
to
use those options - I could patch the source to use a backlog larger
then
511.

This example also works well:

server {
listen *:80;
server_name “test_a”;
return 200 “listen test_a”;
}

server {
listen *:80;
server_name “test_b”;
return 200 “listen test_b”;
}

server {
listen *:80 default_server;
return 200 “listen *”;
}

But if I change it to this:

server {
listen *:80 backlog=65536 deferred;
server_name “test_a”;
return 200 “listen test_a”;
}

server {
listen *:80 backlog=65536 deferred;
server_name “test_b”;
return 200 “listen test_b”;
}

server {
listen *:80 backlog=65536 deferred default_server;
return 200 “listen *”;
}

Then I get the error message “nginx: [emerg] duplicate listen options
for
0.0.0.0:80 in /etc/nginx/nginx.conf”. I can fix it by doing something
like
this:

server {
listen *:80;
server_name “test_a”;
return 200 “listen test_a”;
}

server {
listen *:80;
server_name “test_b”;
return 200 “listen test_b”;
}

server {
listen *:80 backlog=65536 deferred default_server;
return 200 “listen *”;
}

From the ss -l output I am picking up the larger listen queue which I’m
happy about, though its confusing why nginx is picking them from that
last
server stanza (it has the same behavior without the default_server
keyword). If I’m doing a virtual hosting type setup and I’m including
all
of my server definitions from individual files in a subdirectory, it
appears that any one of them could bump up the backlog, but if any two
server stanzas have options to do it then it causes an error. Maybe the
best way to do it is to have some sort of dummy entry that sets the
options

  • if its always the last server stanza that sets the listen options then
    maybe include all the other server stanzas and have the dummy at the
    end
    that sets the backlog and deferred options?

On Fri, Apr 29, 2016 at 05:12:44PM -0400, CJ Ess wrote:

Hi there,

server {
    listen 8088;
    return 200 "listen 8088\n";
}

In that case I get errors like “nginx: [emerg] bind() to 0.0.0.0:8088
failed (98: Address already in use)”. So the workaround is obviously not to
use those options - I could patch the source to use a backlog larger then
511.

That is covered (not necessarily explicitly) at
Module ngx_http_core_module. There’s a section on “parameters specific to
socket-related system calls”; and the description of “bind” indicates
that it can be applied implicitly.

If your OS does not allow you to bind() to both IP:port and to
INADDR_ANY:8088, then the above config (which says to bind individually
to
127.0.0.1:8088 and 10.0.1.2:8088 and 0.0.0.0:8088) is broken on your OS.

 listen *:80 backlog=65536 deferred;
 listen *:80 backlog=65536 deferred;
 listen *:80  backlog=65536 deferred default_server;

Then I get the error message “nginx: [emerg] duplicate listen options for
0.0.0.0:80 in /etc/nginx/nginx.conf”.

“…only once for a given address:port pair”.

 listen *:80;
 listen *:80;
 listen *:80 backlog=65536 deferred default_server;

From the ss -l output I am picking up the larger listen queue which I’m
happy about, though its confusing why nginx is picking them from that last
server stanza (it has the same behavior without the default_server
keyword).

I think it’s clear why it happens, when you know what system calls are
used in response to the config.

I’m not sure how the documentation could be enhanced for clarity without
becoming unwieldy.

If I’m doing a virtual hosting type setup and I’m including all
of my server definitions from individual files in a subdirectory, it
appears that any one of them could bump up the backlog, but if any two
server stanzas have options to do it then it causes an error.

There are lots of other things that could be put into the files that
would be invalid configurations too.

Maybe the
best way to do it is to have some sort of dummy entry that sets the options

  • if its always the last server stanza that sets the listen options then
    maybe include all the other server stanzas and have the dummy at the end
    that sets the backlog and deferred options?

Once upon a time, only the default_server entry could set them (but
it wasn’t called default_server then). Since that is a once-per-port
option too, perhaps you could make a policy of only setting them in
that server{}?

You’d still have the issue of multiple bind()s; but that is presumably
“change your config design or change your OS”.

f

Francis D. [email protected]