Wie kann ich in nginx SSL erzwingen oder umleiten?


222

Ich habe eine Anmeldeseite in einer Subdomain wie: https://signup.example.com

Es sollte nur über HTTPS zugänglich sein, aber ich fürchte, die Leute könnten irgendwie über HTTP darauf stoßen und eine 404 bekommen.

Mein HTML / Server-Block in Nginx sieht folgendermaßen aus:

html {
  server {
    listen 443;
    server_name signup.example.com;

    ssl                        on;
    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;

    ssl_session_timeout 30m;

    location / {
      root /path/to/my/rails/app/public;
      index index.html;
        passenger_enabled on;
    }
  }
}

Was kann ich hinzufügen, damit Personen, die weitergeleitet werden http://signup.example.com, weitergeleitet werden https://signup.example.com? (Zu Ihrer Information, ich weiß, dass es Rails-Plugins gibt, die erzwingen können, SSLaber ich hatte gehofft, dies zu vermeiden.)


Antworten:


145

Laut den Fallstricken von Nginx ist es etwas besser, die unnötige Erfassung wegzulassen und $request_uristattdessen zu verwenden. Fügen Sie in diesem Fall ein Fragezeichen hinzu, um zu verhindern, dass Nginx Abfrageargs verdoppelt.

server {
    listen      80;
    server_name signup.mysite.com;
    rewrite     ^   https://$server_name$request_uri? permanent;
}

68
Oder laut der von Ihnen verlinkten Seite "BESSER" :return 301 http://domain.com$request_uri;
nh2

13
ein Kommentar. Der $ Servername $ nimmt die erste Servername-Variable auf. Beachten Sie dies, wenn Ihre Konfiguration Namen ohne FQN enthält
engineerDave

2
@ nh2 Dies ist ein weiterer Fall, in dem die Dokumentation falsch ist, da die Verwendung return 301...einen Fehler "zu viele Umleitungen" verursacht, während die Umschreibemethode tatsächlich funktioniert.
Mike Bethany

1
Das ist jetzt als "auch schlecht" dokumentiert. @MikeBethany return 301funktioniert, es sei denn (ich vermute), Sie lösen es auch für korrekte URLs aus, indem Sie beide Ports abhören (Beispiel für die Konfiguration, die das Problem auslöst: Nehmen Sie die erste Antwort von serverfault.com/a/474345/29689 und lassen Sie das if weg ).
Blaisorblade

1
Ich frage mich, was sich im Laufe der Jahre geändert hat und ob diese andere Antwort besser ist: serverfault.com/a/337893/119666
Ryan

256

Der beste Weg, wie er im offiziellen How-to beschrieben wurde, ist die Verwendung der returnDirektive:

server {
    listen      80;
    server_name signup.mysite.com;
    return 301 https://$server_name$request_uri;
}

5
kürzeste Antwort und funktionierte perfekt in meinem Fall
mateusz.fiolka

1
Dies wird im Allgemeinen empfohlen, da es a zurückgibt 301 Moved Permanently(Ihre Links wurden dauerhaft verschoben) sowie ein erneutes Schreiben
sgb

1
Dies funktioniert nicht, da es einen Fehler "zu viele Weiterleitungen" verursacht, selbst wenn Sie festgelegt habenproxy_set_header X-Forwarded-Proto https;
Mike Bethany

1
@MikeBethany definierst du listen 443;im selben Block?
Joe B

2
Dies sollte die akzeptierte Antwort sein.
Sjas

119

Dies ist die richtige und effizienteste Methode, wenn Sie alles in einem Serverblock speichern möchten:

server {
    listen   80;
    listen   [::]:80;
    listen   443 default_server ssl;

    server_name www.example.com;

    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;

    if ($scheme = http) {
        return 301 https://$server_name$request_uri;
    }
}

Alles andere oben, mit "rewrite" oder "if ssl_protocol" usw., ist langsamer und schlimmer.

Dies ist das gleiche, aber noch effizienter, da nur das Umschreiben im http-Protokoll ausgeführt wird und nicht bei jeder Anforderung die Variable $ scheme überprüft werden muss. Aber im Ernst, es ist so eine Kleinigkeit, dass Sie sie nicht trennen müssen.

server {
    listen   80;
    listen   [::]:80;

    server_name www.example.com;

    return 301 https://$server_name$request_uri;
}
server {
    listen   443 default_server ssl;

    server_name www.example.com;

    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;
}

8
Großartig, ein Feigling hat diese Antwort abgelehnt, ohne zu sagen warum, obwohl diese Antwort richtig ist. Vielleicht noch einer dieser Kultisten, wenn es böse ist. Wenn Sie sich die Mühe machen, die Nginx-Dokumentation zu If zu lesen, wissen Sie, dass IfIsNOTEvil nur BESTIMMT in einem Standortkontext verwendet wird, den wir hier nicht ausführen. Meine Antwort ist absolut richtig!
DELETEDACC

2
Ich habe dies nicht abgelehnt, möchte jedoch darauf hinweisen, dass die Standardeinstellung in den letzten Versionen in 'default_server' geändert wurde.
Spuder

Die erste Lösung kann nicht die effizienteste sein, wenn die zweite noch effizienter ist. Und Sie haben sogar beschrieben, warum Sie ein if nicht verwenden sollten: "Es muss nicht bei jeder Anfrage die Variable $ scheme überprüft werden". Wenn Sie ifs nicht verwenden, geht es nicht nur um Leistung, sondern auch darum, deklarativ und nicht imperativ zu sein.
Pepkin88

+1 für if ($ scheme = http)
Fernando Kosh

Sollte hier $ host verwenden, wie in den anderen Antworten erwähnt.
Artem Russakovskii

56

Wenn Sie die neue duale HTTP- und HTTPS-Serverdefinition verwenden, können Sie Folgendes verwenden:

server {
    listen   80;
    listen   [::]:80;
    listen   443 default ssl;

    server_name www.example.com;

    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;

    if ($ssl_protocol = "") {
       rewrite ^   https://$server_name$request_uri? permanent;
    }
}

Dies scheint für mich zu funktionieren und führt nicht zu Umleitungsschleifen.

Bearbeiten:

Ersetzt:

rewrite ^/(.*) https://$server_name/$1 permanent;

mit Pratiks Rewrite-Zeile.


2
@DavidPashley Deine Lösung hat wie ein Zauber für mich gewirkt. Vielen Dank
Jayesh Gopalan

1
If you are using the new dual HTTP and HTTPS server definitiondann solltest du es trennen.
VBart

2
elegant und funktioniert perfekt!
Jacktrade

2
Dies war die einzige Lösung, die mit meiner Laravel / Homestead Nginx-Konfiguration funktioniert hat.
Jared Eitnier

1
Auch die Rewrite-Zeile sollte sein, return 301 https://$server_name$request_uri;da dies die bevorzugte Methode ist.
Jared Eitnier

27

Eine weitere Variante, die den Host: request-Header beibehält und dem "GOOD" -Beispiel für Nginx-Fallstricke folgt :

server {
    listen   10.0.0.134:80 default_server;

    server_name  site1;
    server_name  site2;
    server_name  10.0.0.134;

    return 301 https://$host$request_uri;
}

Hier sind die Ergebnisse. Beachten Sie, dass die Verwendung von $server_nameanstelle von $hostimmer zu umleiten würde https://site1.

# curl -Is http://site1/ | grep Location
Location: https://site1/

# curl -Is http://site2/ | grep Location
Location: https://site2/


# curl -Is http://site1/foo/bar | grep Location
Location: https://site1/foo/bar

# curl -Is http://site1/foo/bar?baz=qux | grep Location
Location: https://site1/foo/bar?baz=qux

Note that using $server_name instead of $host would always redirect to https://site1ist das nicht was $request_urifür?
Jürgen Paul

2
$request_uriEnthält keinen Host- oder Domainnamen. Mit anderen Worten, es beginnt immer mit einem "/" Zeichen.
Peter

2
Beste Antwort bei weitem.
Ashesh

3
Ich bin mir nicht sicher, warum diese Antwort so wenig Stimmen hat. Es ist die einzige, die es wert ist, benutzt zu werden.
Zopieux

2
Ich kann nicht glauben, dass so viele Leute $ server_name verwenden. Dies ist der richtige Weg, dies zu tun
Greg Ennis,

3

Stellen Sie sicher, dass Sie für alle Cookies die Option "sicher" aktivieren, da sie sonst bei der HTTP-Anforderung gesendet werden und von einem Tool wie Firesheep abgerufen werden können.


1
server {
    listen x.x.x.x:80;

    server_name domain.tld;
    server_name www.domian.tld;
    server_name ipv4.domain.tld;

    rewrite     ^   https://$server_name$request_uri? permanent;
}

Das funktioniert besser, denke ich. xxxx bezieht sich auf die IP Ihres Servers. Wenn Sie mit Plesk 12 arbeiten, können Sie dies tun, indem Sie die Datei "nginx.conf" im Verzeichnis "/var/www/vhosts/system/domain.tld/conf" für die gewünschte Domain ändern. Vergessen Sie nicht, den Nginx-Dienst neu zu starten, nachdem Sie die Konfiguration gespeichert haben.


rewrite ^ https://$host$request_uri? permanent; wäre eine bessere lösung, da sie mehrere servernamen auf einem vhost haben könnten

0

Ich denke, das ist die einfachste Lösung. Erzwingt sowohl Nicht-HTTPS- als auch Nicht-WWW-Datenverkehr nur zu HTTPS und WWW.

server {
    listen 80;
    listen 443 ssl;

    server_name domain.tld www.domain.tld;

    # global HTTP handler
    if ($scheme = http) {
        return 301 https://www.domain.tld$request_uri;
    }

    # global non-WWW HTTPS handler
    if ($http_host = domain.tld) {
        return 303 https://www.domain.tld$request_uri;
    }
}

EDIT - Apr 2018: Lösung ohne IFs finden Sie in meinem Beitrag hier: https://stackoverflow.com/a/36777526/6076984


1
Werden IF-Bedingungen in der Nginx-Welt nicht als böse und ineffizient angesehen?
PKHunter

Ja, das sind sie im Allgemeinen. Aber für diese einfachen Prüfungen würde ich nicht raten. Ich habe eine richtige Konfigurationsdatei, die zwar mehr Code-Schreiben beinhaltet, aber IFs vollständig vermeidet.
Stempel

Google empfiehlt die Verwendung von 301 anstelle von 303. Quelle: support.google.com/webmasters/answer/6073543?hl=de
dylanh724

@DylanHunt - Ich verließ 303 nur für die Prüfung, nehmen Sie zur Kenntnis , dass der 1. Handler 301 gesetzt wurde, nur 2. ich zu ändern vergessen :) Auch Lösung w / o IFs: stackoverflow.com/a/36777526/6076984
stamster
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.