Entfernen Sie "www" und leiten Sie mit nginx zu "https" um

57

Ich möchte eine Regel in Nginx erstellen, die zwei Dinge tut:

  1. Entfernt das "www." aus der Anfrage-URI
  2. Leitet zu "https" um, wenn der Anforderungs-URI "http" lautet

Es gibt viele Beispiele, wie man diese Dinge einzeln macht, aber ich kann keine Lösung finden, die beides richtig macht (dh keine Umleitungsschleife erstellt und alle Fälle richtig behandelt).

Es muss all diese Fälle behandeln:

1. http://www.example.com/path
2. https://www.example.com/path
3. http://example.com/path
4. https://example.com/path

Diese sollten alle ohne Schleife bei https://example.com/path (# 4) landen . Irgendwelche Ideen?

Devin
quelle
Ich habe gerade www.mydomain.com auf DNS-Ebene zu mydomain.com umgeleitet und in nginx eine 301 für Nicht-https zu https hinzugefügt. Scheint, als ob das in Ordnung sein sollte ¯ \ _ (ツ) _ / ¯
Jonathanbell

Antworten:

94

Der beste Weg, dies zu erreichen, besteht darin, drei Serverblöcke zu verwenden: einen, um http zu https umzuleiten, einen, um den https-www-Namen zu no-www umzuleiten, und einen, um tatsächlich Anfragen zu bearbeiten. Der Grund für die Verwendung zusätzlicher Serverblöcke anstelle von ifs ist, dass die Serverauswahl mithilfe einer Hash-Tabelle erfolgt und sehr schnell ist. If auf Serverebene bedeutet, dass if für jede Anforderung ausgeführt wird, was verschwenderisch ist. Außerdem ist das Erfassen der angeforderten URL beim Umschreiben verschwenderisch, da nginx diese Informationen bereits in den Variablen $ uri und $ request_uri (jeweils ohne und mit Abfragezeichenfolge) enthält.

server {
    server_name www.example.com example.com;
    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl;
    ssl_certificate /path/to/server.cert;
    ssl_certificate_key /path/to/server.key;
    server_name www.example.com;
    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl;
    ssl_certificate /path/to/server.cert;
    ssl_certificate_key /path/to/server.key;
    server_name example.com;

    <locations for processing requests>
}
kolbyjack
quelle
2
Ist der mittlere Block notwendig? Schreibt der erste Block nicht schon von www nach nicht www?
pbreitenbach
3
Der erste Block verarbeitet nur http. Der mittlere Block ist erforderlich, um https-Anforderungen von https: // www.example.com/ an https: // example.com/ umzuleiten. (Entschuldigung für die zusätzlichen Leerzeichen, die https werden sonst nicht
angezeigt
1
nur eine kleine Formatierungsnotiz - wenn Sie keinen Link erstellen möchten, können Sie Kommentartext in Anführungszeichen setzen `, das unter Tilde. Es würde so aussehen:https://example.com/
Cyclops
9
Der zweite Block benötigt ebenfalls cert info.
Ricka
3
Als ich diese Antwort versuchte, stieß ich auf ein anderes Problem. Ich dachte, ich könnte 301 von www.sub.example.comnach umleiten sub.example.comund sub.example.combekomme dann erst ein SSL-Zertifikat für Jetzt weiß ich, dass die SSL-Zertifikatsüberprüfung vor der 301-Umleitung stattfindet, daher kann es nicht funktionieren. Weitere Erklärungen finden Sie hier: serverfault.com/a/358625/144811
Gruzzles
11

Das funktioniert bei mir:

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

server {
    listen              443 ssl;
    server_name         www.yourdomain.com;
    ssl_certificate     /path/to/certificate.crt;
    ssl_certificate_key /path/to/private/key.pem;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    return              301 https://yourdomain.com$request_uri;
}

server {
    listen              443 ssl;
    server_name         yourdomain.com;
    ssl_certificate     /path/to/certificate.crt;
    ssl_certificate_key /path/to/private/key.pem;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;

    # do the proper handling of the request
}

Beachten Sie, dass beide yourdomain.com und www.yourdomain.com muss in dem SSL - Zertifikat sein. Dies ist mit einem Platzhalterzertifikat oder mit einem alternativen Servernamen möglich, wie hier erläutert . Überprüfen Sie https://www.startssl.com für nette und kostenlose Zertifikate, die dies tun. ( Edith : Ab Chrome-Version 56 werden startssl-Zertifikaten nicht mehr vertraut. Versuchen Sie stattdessen https://letsencrypt.org/ .)

e18r
quelle
Dieses funktioniert tatsächlich, aber ich dachte, es könnte klarer gemacht werden, ohne viele doppelte Konfigurationszeilen.
Zloynemec
@zloynemec Sie können das SSL-Zeug in eine separate .conf-Datei einfügen und die includeRegel verwenden, um es beiden SSL- Serverblöcken hinzuzufügen.
Igettäjä
Wenn Sie Cloudflare verwenden, müssen Sie das Zertifikat für 10 USD / Monat bezahlen, um die 2 Subdomains (www + etwas) umleiten und proxen zu können. Lassen Sie mich wissen, ob es eine Problemumgehung gibt.
Freedo
7

Nachdem ich so viel Zeit mit Hunderten von ähnlichen Fällen verbracht habe, habe ich mir den folgenden Ausschnitt ausgedacht. Es ist kurz und kann leicht an alles angepasst werden.

server {
    listen 80;
    listen 443 ssl;
    server_name example.com www.example.com;
    ssl_certificate /path/to/my/certs/example.com/fullchain.pem;
    ssl_certificate_key /path/to/my/certs/example.com/privkey.pem;

    # Redirect to the correct place, if needed
    set $https_redirect 0;
    if ($server_port = 80) { set $https_redirect 1; }
    if ($host ~ '^www\.') { set $https_redirect 1; }
    if ($https_redirect = 1) {
        return 301 https://example.com$request_uri;
    }

    location / {
    # ...
}

Oh aber ifist böse !

Ja, das kann sein. Aber es gibt einen Grund und sollte denen, die wissen, wie man es richtig benutzt, keinen Schaden zufügen. ;)

Emyller
quelle
Ich mag das, aber hast du irgendwelche Daten über den Leistungstreffer? Danke!
Freedo
1
Ehrlich gesagt habe ich das nie verglichen, aber ich glaube, dass es im Vergleich zu separaten Regeln kaum Auswirkungen geben würde, da der Effekt ziemlich gleich ist.
Emyller
Benchmark bei Umleitung? Es ist nicht wirklich relevant, nein? (wahre Frage, kein Troll ^^)
Matrix
3

Ich ziehe es vor, mit einem Antwortcode zurückzukehren, damit der Browser weiß, dass Sie ihn zu einer anderen URL umleiten.

server {
    listen   80;
    server_name  www.example.com;

    return 301 https://example.com$request_uri;
}

dann blockiert eine andere Serverkonfiguration für den https

server {
        listen   443 ssl;
        server_name  example.com;
        ...
    }
montss
quelle
0

Wie wäre es, einen Serverblock für diesen Zweck zu erstellen:

server{
    listen 80;
    server_name www.example.net example.net;
    rewrite ^(.*) https://example.net$1 permanent;
}

Starten Sie dann nginx neu

Anthonysomerset
quelle
Beim Neustart wird eine Fehlermeldung angezeigt, dass der Servername in Konflikt steht. Außerdem wartet dieser Befehl nicht auf Port 443 auf SSL, und ich muss mich auch um die Umleitung https://www.example.comzu kümmern https://example.com.
Devin
0

Ich denke das sollte funktionieren.

In Ihrer normalen HTTP-Serverdefinition hat anthonysomerset Folgendes vorgeschlagen:

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

Dann auf Ihrer SSL-Serverdefinition:

if ($host ~ /^www\./) {
  rewrite ^(.*) https://example.net$1 permanent;
}

Auf diese Weise sollte die Weiterleitung nur einmal pro Anfrage erfolgen, unabhängig davon, zu welcher URL der Benutzer ursprünglich wechselt.

Eduardo Ivanec
quelle
Das hat funktioniert, danke. Ich musste Ihre Bedingung auf ändern, if ($host = 'www.example.com') {da Ihr Regex für mich nicht funktionierte. Keine Ahnung warum, da es richtig aussieht.
Devin
Beachten Sie, dass, wenn es böse ist und es im Allgemeinen besser ist, deklarativ vorzugehen.
Blaise
0

Hier ist das vollständige Beispiel, das für mich funktioniert hat. Das Problem war, dass ich die SSL-Details ( ssl_certificateusw.) nicht im WWW- Umleitungsblock hatte. Denken Sie daran, Ihre Protokolle zu überprüfen ( sudo tail -f /var/log/nginx/error.log)!

# HTTP — redirect all traffic to HTTPS
server {
    listen 80;
    listen [::]:80 default_server ipv6only=on;
    return 301 https://$host$request_uri;
}

# HTTPS — redirects www to non-www
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name www.example.com;

    # Use the Let's Encrypt certificates
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Include the SSL configuration from cipherli.st
    include snippets/ssl-params.conf;
    return 301 https://example.com$request_uri;
}

# HTTPS — proxy all requests to the app (port 3001)
server {
    # Enable HTTP/2
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com sub.example.com;

    # Use the Let's Encrypt certificates
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Include the SSL configuration from cipherli.st
    include snippets/ssl-params.conf;

    # For LetsEncrypt:
    location ~ /.well-known {
        root /var/www/html;
        allow all;
    }

    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-NginX-Proxy true;
        proxy_pass http://localhost:3001;
        proxy_ssl_session_reuse off;
        proxy_set_header Host $http_host;
        proxy_cache_bypass $http_upgrade;
        proxy_redirect off;
    }
}

quelle