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


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

  1. Entfernt das "www." aus der Anfrage URI
  2. Weist auf "https" um, wenn der Anfrage-URI "http" lautet

Es gibt viele Beispiele, wie man jedes dieser Dinge individuell macht, aber ich kann keine Lösung finden, die beides richtig macht (d. H. Keine Umleitungsschleife erzeugt und alle Fälle richtig behandelt).

Es muss alle 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 enden https://beispiel.com/pfad (# 4) ohne Schleife. Irgendwelche Ideen?


42
2018-04-11 16:37


Ursprung


Ich habe gerade www.mydomain.com auf DNS-Ebene zu mydomain.com umgeleitet und ein 301 für Nicht-HTTPS zu HTTPS in NGINX hinzugefügt. Scheint so, das sollte in Ordnung sein ¯ \ _ (ツ) _ / ¯ - jonathanbell


Antworten:


Der beste Weg, dies zu erreichen, besteht darin, drei Serverblöcke zu verwenden: einen, um HTTP zu https umzulenken, einen, um den https www-Namen auf no-www umzuleiten, und einen, um Anfragen tatsächlich zu bearbeiten. Der Grund für die Verwendung zusätzlicher Serverblöcke anstelle von ifs ist, dass die Serverauswahl unter Verwendung einer Hash-Tabelle durchgeführt wird und sehr schnell ist. Wenn eine Serverebene verwendet wird, bedeutet dies, dass für jede Anforderung das if ausgeführt wird, was eine Verschwendung darstellt. Außerdem ist das Erfassen des angeforderten uri beim Neuschreiben unwirtschaftlich, da nginx diese Informationen bereits in den Variablen $ uri und $ request_uri (ohne bzw. mit Abfragezeichenfolge) hat.

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>
}

61
2018-04-11 18:21



Ist der Mittelblock notwendig? Ist der erste Block nicht schon von www auf non-www umgeschrieben? - pbreitenbach
Der erste Block behandelt nur http. Der mittlere Block ist notwendig, um HTTPS-Anfragen von https: // www.example.com/ zu https: // example.com/ umzuleiten. (Sorry für die zusätzlichen Leerzeichen, ich kann es nicht zeigen, die HTTPS sonst) - kolbyjack
nur ein kleiner Formatierungshinweis - wenn Sie vermeiden möchten, einen Link zu machen, können Sie den Kommentartext in Back-Anführungszeichen `setzen, der unter Tilde. Es würde sich zeigen wie: https://example.com/ - Cyclops
Der zweite Block benötigt auch Cert-Informationen. - ricka
Als ich diese Antwort versuchte, stieß ich auf ein anderes Problem. Dachte ich könnte 301 umleiten von www.sub.example.com zu sub.example.com und dann nur ein SSL-Zertifikat für erhalten sub.example.com Jetzt weiß ich, dass ssl cert Überprüfung vor der 301-Weiterleitung passiert, so kann es nicht funktionieren. Mehr Erklärung hier: serverfault.com/a/358625/144811 - Gruzzles


Das funktioniert für mich:

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
}

Denk daran, dass beide  yourdomain.com  und  www.yourdomain.com  Muss in Ihrem SSL-Zertifikat sein. Dies ist mit einem Platzhalterzertifikat oder mit einem alternativen Servernamen wie erläutert möglich Hier. Prüfen https://www.startssl.com für nette und kostenlose Zertifikate, die dies tun. (Edith: Beginnend mit der Chrome-Version 56 wird den Start-SSL-Zertifikaten nicht mehr vertraut. Versuchen https://letsencrypt.org/ stattdessen.)


8
2018-05-09 15:45



Dieser funktioniert tatsächlich, aber ich dachte, es könnte auf eine klarere Art und Weise ohne viele doppelte Konfigurationszeilen gemacht werden. - zloynemec
@zloynemec Sie könnten den SSL-Code in eine separate .conf-Datei schreiben und den include Regel, um es beiden SSL-Serverblöcken hinzuzufügen. - Igettäjä
Auch wenn Sie cloudflare verwenden, müssen Sie das $ 10 / mo-Zertifikat bezahlen, um die 2 Subdomains (www + something) umleiten zu können. Lassen Sie mich wissen, ob es einen Workaround gibt. - Freedo


Nachdem ich so viel Zeit mit Hunderten von ähnlichen Fällen verbracht habe, habe ich es mir ausgedacht das folgende Snippet. Es ist kurz und kann leicht angepasst werden, um alles anzupassen.

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 doch if ist böse!

ja, es können Sein. Aber es existiert aus einem bestimmten Grund und sollte denen, die es tun, keinen Schaden zufügen weiß, wie man es richtig benutzt. ;)


6
2018-01-24 14:11



Ich mag das, aber haben Sie irgendwelche Daten über den Performance-Hit? Vielen Dank! - Freedo
Ehrlich gesagt, habe ich das nie bewertet, aber ich glaube, dass es im Vergleich zu separaten Regeln kaum Auswirkungen geben würde, da der Effekt ziemlich gleich ist. - emyller


Ich ziehe es vor, mit einem Antwortcode zurückzukehren, damit der Browser weiß, dass Sie ihn auf eine andere URL umleiten.

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

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

dann ein weiterer Serverkonfigurationsblock für die https

server {
        listen   443 ssl;
        server_name  example.com;
        ...
    }

3
2017-09-18 12:16





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;
}

dann nginx neu starten


0
2018-04-11 16:45



Ich bekomme einen "widersprüchlichen Servernamen" Fehler beim Neustart. Außerdem wird dieser Befehl Port 443 nicht auf SSL überwachen und ich muss mich um die Weiterleitung kümmern https://www.example.com zu https://example.com auch. - Devin


Ich denke, das sollte funktionieren.

Auf Ihrer einfachen HTTP-Server-Definition etwas wie Anthonysomerset vorgeschlagen, das heißt:

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

Dann auf Ihrer SSL-Server-Definition:

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 weitergeleitet wurde.


0
2018-04-11 17:56



Das hat funktioniert, danke. Ich musste deine Kondition ändern if ($host = 'www.example.com') { aber deine Regex funktionierte nicht für mich. Keine Ahnung warum, wie es richtig aussieht. - Devin
Beachten Sie das wenn es böse ist und es ist im Allgemeinen besser, eine deklarative Art zu verwenden. - Blaise


Hier ist das vollständige Beispiel, das für mich funktioniert hat. Das Problem war, dass ich nicht die ssl Details hatte (ssl_certificateusw.) im WWW-Redirect-Block. 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;
    }
}

0
2018-02-07 03:58