Warum reagiert nginx auf einen Domainnamen?

139

Ich habe Nginx mit einer Ruby / Sinatra-App eingerichtet und alles ist in Ordnung. Jetzt versuche ich jedoch, eine zweite Anwendung auf demselben Server auszuführen, und habe etwas Seltsames bemerkt. Hier ist zuerst meine nginx.conf:

pid /tmp/nginx.pid;
error_log /tmp/nginx.error.log;

events {
  worker_connections 1024;
  accept_mutex off;
}

http {
  default_type application/octet-stream;
  access_log /tmp/nginx.access.log combined;

  sendfile on;
  tcp_nopush on;
  tcp_nodelay off;

  gzip on;
  gzip_http_version 1.0;
  gzip_proxied any;
  gzip_min_length 500;
  gzip_disable "MSIE [1-6]\.";
  gzip_types text/plain text/xml text/css
             text/comma-separated-values
             text/javascript application/x-javascript
             application/atom+xml;

  upstream app {
    server unix:/var/www/app/tmp/sockets/unicorn.sock fail_timeout=0;
  }

  server {
    listen 80;
    client_max_body_size 4G;
    server_name FAKE.COM;

    keepalive_timeout 5;

    root /var/www/app/public;

    location / {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;

      if (!-f $request_filename) {
        proxy_pass http://app;
        break;
      }
    }

    error_page 500 502 503 504 /500.html;
    location = /500.html {
      root /var/www/app/public;
    }
  }
}
                                                          68,0-1        B

Beachten Sie, wie server_nameeingestellt ist, dass FAKE.COMder Server auf alle Hosts reagiert, die diesen Server über andere Domänennamen erreichen. Wie kann ich diesen bestimmten Server dazu bringen, nur auf Anfragen zu antworten FAKE.COM?

Martin
quelle
der listen fake.com | something.com:80 Befehl filtert nicht server_name.
Alexei Martchenko

Antworten:

202

Der erste Serverblock in der nginx-Konfiguration ist die Standardeinstellung für alle Anforderungen, die den Server treffen, für den es keinen bestimmten Serverblock gibt.

Angenommen, Ihre reale Domain ist REAL.COM. Wenn ein Benutzer dies eingibt, wird dies in Ihren Server aufgelöst. Da für dieses Setup kein Serverblock vorhanden ist, ist der Serverblock für FAKE.COM der erste Serverblock (in Ihrem Fall nur Serverblock) verarbeitet diese Anforderung.

Aus diesem Grund haben richtige Nginx-Konfigurationen einen bestimmten Serverblock für Standardeinstellungen, bevor andere für bestimmte Domänen verwendet werden.

# Default server
server {
    return 404;
}

server {
    server_name domain_1;
    [...]
}

server {
    server_name domain_2;
    [...]
}

etc

** BEARBEITEN **

Es scheint, dass einige Benutzer durch dieses Beispiel etwas verwirrt sind und denken, dass es auf eine einzelne conf-Datei usw. beschränkt ist.

Bitte beachten Sie, dass das Obige ein einfaches Beispiel für die Entwicklung des OP nach Bedarf ist.

Ich persönlich verwende damit separate vhost conf-Dateien (CentOS / RHEL):

http {
    [...]
    # Default server
    server {
        return 404;
    }
    # Other servers
    include /etc/nginx/conf.d/*.conf;
}

/etc/nginx/conf.d/ enthält domain_1.conf, domain_2.conf ... domain_n.conf, die nach dem Serverblock in die Hauptdatei nginx.conf aufgenommen wird. Dies ist immer die erste und immer die Standardeinstellung, sofern sie nicht mit dem default_server überschrieben wird Richtlinie anderswo.

Die alphabetische Reihenfolge der Dateinamen der conf-Dateien für die anderen Server spielt in diesem Fall keine Rolle.

Darüber hinaus bietet diese Anordnung viel Flexibilität, da mehrere Standardeinstellungen definiert werden können.

In meinem speziellen Fall überwacht Apache Port 8080 nur auf der internen Schnittstelle und überträgt PHP- und Perl-Skripte an Apache.

Ich führe jedoch zwei separate Anwendungen aus, die beide Links mit ": 8080" im angehängten Ausgabe-HTML zurückgeben, da sie feststellen, dass Apache nicht auf dem Standard-Port 80 ausgeführt wird, und versuchen, mir zu "helfen".

Dies führt zu dem Problem, dass die Links ungültig werden, da Apache nicht über die externe Schnittstelle erreichbar ist und die Links auf Port 80 zeigen sollten.

Ich behebe dies, indem ich einen Standardserver für Port 8080 erstelle, um solche Anforderungen umzuleiten.

http {
    [...]
    # Default server block for undefined domains
    server {
        listen 80;
        return 404;
    }
    # Default server block to redirect Port 8080 for all domains
    server {
        listen my.external.ip.addr:8080;
        return 301 http://$host$request_uri;
    }
    # Other servers
    include /etc/nginx/conf.d/*.conf;
}

Da nichts in den regulären Serverblöcken auf Port 8080 lauscht, verarbeitet der Standard-Serverblock zur Umleitung solche Anforderungen aufgrund seiner Position in nginx.conf transparent.

Ich habe tatsächlich vier solcher Serverblöcke und dies ist ein vereinfachter Anwendungsfall.

Dayo
quelle
1
Nginx erfordert Groß- und Kleinschreibung. "Server" sollte Server sein und "Return" sollte Return sein. Hoffe, dies erspart ein paar Probleme beim Kopieren dieses Codes.
Capaj
2
statisch, siehe Antwort von Oleg Neumyvkin - Wenn Sie mehrere Konfigurationsdateien auf Websites zur Verfügung haben, ist der erste Server in der ersten Datei in alphabetischer Reihenfolge Ihre Standardeinstellung. Ich vermute, dass dies ein Problem sein könnte. Außerdem führen viele Distributionen ein 'nginx -t' aus, um die Konfiguration vor dem Neustart zu testen. Möglicherweise liegt ein Fehler vor, der einen Neustart verhindert.
Jwhitlock
2
Dies ist unvollständig und sollte nicht die akzeptierte Antwort sein. Damit dies funktioniert, müssten Sie auch default_server aus allen Listen-Direktiven entfernen.
Ben
@ben Erstens hatte das OP in seinem Problembeispiel keinen "default_server" und die Antwort ist auf die Besonderheiten dieser Frage zugeschnitten. Zweitens, warum jemand default_server auf einem separaten Serverstandort definieren würde, wenn er den Anweisungen folgt, um den ersten definierten Server zum Standard zu machen, ist mir ein Rätsel. In jedem Fall, wenn ein default_server speziell definiert wurde, sollte dieser Q / A-Satz nicht berücksichtigt werden, um eventuelle Probleme zu lösen.
Dayo
1
@Dayo Sie haben Recht, dass Sie die vom OP veröffentlichte spezifische Konfiguration angesprochen haben. Für eine vollständige Beantwortung der gestellten Fragen halte ich es jedoch für notwendig, den default_server zu erwähnen. Es ist sehr gut möglich, Ihre Antwort zu lesen und nicht zu erkennen, wie default_server sie stören würde. Dies ist sogar noch wahrscheinlicher, da einige Distributionen mit default_server ausgeliefert werden, die in einer Datei definiert sind, die für den Benutzer möglicherweise nicht offensichtlich ist.
Ben
61

Sie sollten einen Standardserver für Catch-All haben. Sie können zurückkehren 404oder besser gar nicht antworten (spart etwas Bandbreite), indem Sie 444eine nginxspezifische HTTP-Antwort zurückgeben, die einfach die Verbindung schließt und nichts zurückgibt

server {
    listen       80  default_server;
    server_name  _; # some invalid name that won't match anything
    return       444;
}
iTech
quelle
Arbeitete für mich für Nginx 1.8.0. Das hatte server_name _;ich in früheren Versionen für Nginx nicht, aber es hat funktioniert. Für die neueren Nginx-Versionen scheint es, dass Sie die benötigen server_name _;. Danke
Daniel
Gelöst. Ich habe ein Semikolon vergessen; _;
user1201917
2
@iTech: Aus irgendeinem Grund wird für alle Anfragen 444 zurückgegeben. Irgendwelche Hinweise?
Divick
Toller Tipp über die 444und imho eine viel sauberere Lösung als die Rückgabe eines Fehlercodes.
Qqilihq
1
Es ist wichtig, ein Zertifikat / einen Schlüssel anzugeben, da sonst alle SSL-Verbindungen übereinstimmen und fehlschlagen, wie @AndreyT in seiner Antwort unten ausführt.
Mark Fletcher
34

Ich konnte mein Problem mit keiner der anderen Antworten lösen. Ich habe das Problem behoben, indem ich überprüft habe, ob der Host übereinstimmt, und einen 403 zurückgegeben habe, wenn dies nicht der Fall war. (Ich hatte eine zufällige Website, die auf den Inhalt meines Webservers zeigte. Ich schätze, den Suchrang zu entführen.)

server {
    listen 443;
    server_name example.com;

    if ($host != "example.com") {
        return 403;
    }

    ...
}
Matt Carrier
quelle
1
Wenn eine schlechte Praxis sind: nginx.com/resources/wiki/start/topics/depth/ifisevil
Esolitos
1
Es gibt Fälle, in denen Sie die Verwendung eines if einfach nicht vermeiden können, wenn Sie beispielsweise eine Variable testen müssen, für die es keine entsprechende Anweisung gibt.
Edward
4
@Esolitos Die dritte Zeile dieses Artikels besagt buchstäblich, dass dieser Anwendungsfall in Ordnung ist. Ich verstehe die Notwendigkeit, vorsichtig zu sein, aber lassen Sie uns bei vernünftigen Anwendungsfällen nicht mit den Fingern wedeln.
mpowered
Meiner Meinung nach die einfachste und prägnanteste Lösung, da nur 3 Codezeilen erforderlich sind, die Sie gerne in jede vhost-Datei einfügen können, und anschließend nur den verglichenen $ host ändern.
Akito
28

Um Ihre Frage zu beantworten, wählt nginx den ersten Server aus, wenn keine Übereinstimmung vorliegt. Siehe Dokumentation :

Wenn sein Wert keinem Servernamen entspricht oder die Anforderung dieses Headerfeld überhaupt nicht enthält, leitet nginx die Anforderung an den Standardserver für diesen Port weiter. In der obigen Konfiguration ist der Standardserver der erste ...

Wenn Sie nun einen Standard-Catch-All-Server haben möchten, der beispielsweise mit 404 auf alle Anforderungen antwortet, gehen Sie wie folgt vor:

server {
    listen 80 default_server;
    listen 443 ssl default_server;
    server_name _;
    ssl_certificate <path to cert>
    ssl_certificate_key <path to key>
    return 404;
}

Beachten Sie, dass Sie ein Zertifikat / einen Schlüssel angeben müssen (der selbstsigniert sein kann), da sonst alle SSL-Verbindungen fehlschlagen, da nginx versucht, eine Verbindung mit diesem Standard-Server zu akzeptieren und kein Zertifikat / Schlüssel findet.

andreycpp
quelle
3
Ich habe keine Ahnung, warum diese Antwort so weit unten auf der Liste steht. Dies ist derjenige, der die Frage beantwortet, ohne dabei von glänzenden Dingen abgelenkt zu werden.
mmc
Dies half mir, ein Problem zu lösen, bei dem ich versuchte, eine Umleitung von Nicht-WWW zu WWW durchzuführen, bei der ich mein SSL-Zertifikat in diese Umleitungsroute aufnehmen musste. Andernfalls wurde versucht, mein Standard-SSL-Zertifikat zu erhalten, das für eine andere Domain bestimmt war.
Endyourif
1
Dies scheint die beste Antwort für die meisten Konfigurationen zu sein. Ich bin mir nicht sicher, wer heutzutage Nginx ohne SSL verwendet, aber die Tatsache, dass dies die einzige ist, die SSL abdeckt, ist äußerst aussagekräftig.
mpowered
Es scheint, dass server_name _;das nicht einmal gebraucht wird.
Julien Salinas
26

Es gibt nur wenige Möglichkeiten, den Standardserver anzugeben.

Erster Weg - Geben Sie den Standardserver zuerst in der Liste an, wenn Sie Ihre Serverkonfigurationen in einer Konfigurationsdatei behalten, wie oben gezeigt.

Zweiter Weg (besser) Flexibler - Geben Sie default_serverParameter für listenAnweisungen an, zum Beispiel:

server {
    listen  *:80 default_server;
    root /www/project/public/;
}

Weitere Informationen hier: Nginx doc / Listen

Diese Methode ist nützlicher, wenn Sie Serverkonfigurationen in separaten Dateien aufbewahren und diese Dateien nicht alphabetisch benennen möchten.

Pavel
quelle
3
Dies sollte die richtige Antwort sein. Die akzeptierte Antwort ist irreführend. Das einfache Hinzufügen eines weiteren Servers zur Konfiguration löst das Problem nicht, wenn ein anderer Server als Standardserver konfiguriert ist.
Ben
1
@Pavel Es gibt keinen Grund, warum die angegebene Antwort nicht mit mehreren separaten vhost-Dateien funktionieren kann. Außerdem weiß ich, dass sich mein Standardserver immer in der Hauptdatei nginx.conf befindet und ich mich nicht daran erinnern muss, welche meiner vielen separaten vhost-Dateien dies enthält.
Dayo
Vergessen Sie nicht, auch 443 für ssl anzuhören (siehe @ AndreyTs Antwort unten).
Constantinos
8

Kleiner Kommentar zur Antwort:

Wenn Sie mehrere virtuelle Hosts auf mehreren IPs in mehreren Konfigurationsdateien in sites-available / haben, wird die "Standard" -Domäne für IP aus der ersten Datei in alphabetischer Reihenfolge übernommen.

Und wie Pavel sagte, gibt es das Argument "default_server" für die Direktive "listen" http://nginx.org/en/docs/http/ngx_http_core_module.html#listen

Oleg Neumyvakin
quelle