Usuń „www” i przekieruj do „https” za pomocą nginx


57

Chcę utworzyć regułę w Nginx, która robi dwie rzeczy:

  1. Usuwa „www”. z URI żądania
  2. Przekierowuje do „https”, jeśli identyfikator URI żądania to „http”

Istnieje wiele przykładów, jak wykonać każdą z tych czynności osobno, ale nie mogę znaleźć rozwiązania, które działa poprawnie (tzn. Nie tworzy pętli przekierowania i poprawnie obsługuje wszystkie przypadki).

Musi obsłużyć wszystkie te przypadki:

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

Wszystko to powinno zakończyć się na https://example.com/path (# 4) bez zapętlania. Jakieś pomysły?


Właśnie przekierowałem www.mydomain.com na mydomain.com na poziomie DNS i dodałem 301 dla non-https do https w nginx. Wygląda na to, że powinno być dobrze ¯ \ _ (ツ) _ / ¯
jonathanbell

Odpowiedzi:


94

Najlepszym sposobem na osiągnięcie tego jest użycie trzech bloków serwera: jeden do przekierowania http na https, jeden do przekierowania nazwy www https na no-www i jeden do obsługi żądań. Powodem użycia dodatkowych bloków serwera zamiast ifs jest to, że wybór serwera jest dokonywany przy użyciu tabeli skrótów i jest bardzo szybki. Korzystanie z poziomu serwera if oznacza, że ​​if jest uruchamiane dla każdego żądania, co jest marnotrawstwem. Również przechwytywanie żądanego identyfikatora URI w przepisywaniu jest marnotrawstwem, ponieważ nginx ma już tę informację w zmiennych $ uri i $ request_uri (odpowiednio bez i z ciągiem zapytania).

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

2
Czy konieczny jest środkowy blok? Czy pierwszy blok nie jest już przepisywany z www na inne niż www?
pbreitenbach

3
Pierwszy blok obsługuje tylko http. Środkowy blok jest niezbędny do przekierowania żądań https z https: // www.example.com/ na https: // example.com/. (Przepraszam za dodatkowe spacje, nie mogę zmusić go do pokazania https inaczej)
kolbyjack

1
tylko drobna uwaga dotycząca formatowania - jeśli chcesz uniknąć tworzenia linku, możesz umieścić tekst komentarza w cudzysłowach `, pod tyldą. Pojawiłby się następująco:https://example.com/
Cyclops

9
drugi blok wymaga również informacji o certyfikacie.
ricka

3
Próbując znaleźć odpowiedź, napotkałem inny problem. Myślałem, że mogę przekierować 301 z www.sub.example.comdo, sub.example.coma następnie uzyskać tylko certyfikat SSL. sub.example.comTeraz wiem, że sprawdzanie certyfikatu ssl odbywa się przed przekierowaniem 301, więc nie może działać. Więcej wyjaśnień tutaj: serverfault.com/a/358625/144811
Gruzzles

11

To działa dla mnie:

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
}

Pamiętaj, że jedno yourdomain.com i drugie www.yourdomain.com musi znajdować się w certyfikacie SSL. Jest to możliwe w przypadku certyfikatu wieloznacznego lub alternatywnej nazwy serwera, jak wyjaśniono tutaj . Sprawdź https://www.startssl.com, aby znaleźć ładne i bezpłatne certyfikaty, które to robią. ( Edith : począwszy od wersji Chrome 56, certyfikaty Startsl nie będą już zaufane. Zamiast tego spróbuj https://letsencrypt.org/ ).


Ten faktycznie działa, ale myślałem, że można to zrobić w bardziej przejrzysty sposób bez wielu zduplikowanych linii konfiguracji.
zloynemec

@zloynemec Możesz umieścić pliki SSL w osobnym pliku .conf i użyć includereguły, aby dodać je do obu bloków serwera SSL.
Igettäjä,

Ponadto, jeśli korzystasz z cloudflare, musisz zapłacić 10 USD za miesiąc, aby móc przekierować i proxy 2 subdomen (www + coś). Daj mi znać, jeśli istnieje obejście tego problemu.
Freedo

7

Po spędzeniu tak dużo czasu z setkami podobnych przypadków wymyśliłem następujący fragment kodu. Jest krótki i można go łatwo dostosować, aby pasował na wszystko.

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 / {
    # ...
}

Och, ale ifjest zły !

Tak, może być. Ale istnieje z jakiegoś powodu i nie powinien wyrządzać szkody tym, którzy wiedzą, jak go właściwie używać. ;)


Podoba mi się to, ale czy masz jakieś dane na temat wydajności? Dziękuję Ci!
Freedo

1
Szczerze mówiąc, nigdy nie analizowałem tego, ale uważam, że nie będzie to miało większego wpływu w porównaniu do oddzielnych zasad, ponieważ efekt jest prawie taki sam.
emyller

test na przekierowanie? to naprawdę nie ma znaczenia nie? (prawdziwe pytanie, nie troll ^^)
Matrix

3

Wolę powrócić z kodem odpowiedzi, aby przeglądarka wiedziała, że ​​przekierowujesz go na inny adres URL.

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

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

następnie inny blok konfiguracji serwera dla https

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

0

co powiesz na utworzenie bloku serwera w tym celu:

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

następnie ponownie uruchamiam nginx


Podczas ponownego uruchamiania pojawia się błąd „nazwa serwera powodującego konflikt”. Ponadto to polecenie nie będzie nasłuchiwało na porcie 443 dla protokołu SSL i muszę się również martwić przekierowaniem https://www.example.comdo https://example.com.
Devin

0

Myślę, że to powinno zadziałać.

W zwykłej definicji serwera HTTP sugerowano coś takiego jak anthonysomerset, to znaczy:

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

Następnie w definicji serwera SSL:

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

W ten sposób przekierowanie powinno nastąpić tylko raz na żądanie, bez względu na adres URL użytkownika, do którego pierwotnie się udaje.


To działało, dzięki. Musiałem jednak zmienić twoje warunki warunkowe, if ($host = 'www.example.com') {ponieważ regex nie działał dla mnie. Nie mam pojęcia dlaczego, ponieważ wygląda poprawnie.
Devin

Zauważ, że jeśli jest złe i ogólnie lepiej jest użyć deklaratywnego sposobu.
Blaise,

0

Oto pełny przykład, który skończył się dla mnie. Problem polegał na tym, że nie miałem szczegółów ssl ( ssl_certificateitp.) W bloku przekierowań www. Pamiętaj, aby sprawdzić swoje dzienniki ( 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;
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.