Rimuovere "www" e reindirizzare a "https" con nginx


57

Voglio creare una regola in nginx che fa due cose:

  1. Rimuove il "www." dall'URI della richiesta
  2. Reindirizza a "https" se l'URI della richiesta è "http"

Ci sono molti esempi di come fare ognuna di queste cose individualmente, ma non riesco a capire una soluzione che faccia entrambe le cose correttamente (cioè non crei un ciclo di reindirizzamento e gestisca correttamente tutti i casi).

Deve gestire tutti questi casi:

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

Tutti questi dovrebbero finire su https://example.com/path (# 4) senza loop. Qualche idea?


Ho appena reindirizzato www.mydomain.com a mydomain.com a livello DNS e ho aggiunto un 301 per non-https a https in nginx. Sembra che dovrebbe andare bene ¯ \ _ (ツ) _ / ¯
jonathanbell

Risposte:


94

Il modo migliore per ottenere ciò è utilizzare tre blocchi server: uno per reindirizzare http su https, uno per reindirizzare il nome www https su no-www e uno per gestire effettivamente le richieste. Il motivo dell'utilizzo di blocchi di server aggiuntivi anziché ifs è che la selezione del server viene eseguita utilizzando una tabella hash ed è molto veloce. L'uso di un livello server if indica se viene eseguito per ogni richiesta, il che è dispendioso. Inoltre, catturare l'uri richiesto nella riscrittura è dispendioso, poiché nginx ha già queste informazioni nelle variabili $ uri e $ request_uri (rispettivamente senza e con stringa di query).

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
È necessario il blocco centrale? Il primo blocco non è già riscritto da www a non www?
pbreitenbach,

3
Il primo blocco gestisce solo http. Il blocco intermedio è necessario per reindirizzare le richieste https da https: // www.example.com/ a https: // example.com/. (Mi dispiace per gli spazi extra, altrimenti non posso far vedere l'https)
kolbyjack

1
solo una piccola nota di formattazione - se vuoi evitare di creare un link, puoi inserire un testo di commento tra virgolette `, quello sotto tilde. Si presenterebbe come:https://example.com/
Ciclope,

9
anche il secondo blocco richiede informazioni di avviso.
ricka,

3
Cercando questa risposta, ho riscontrato un altro problema. Pensavo di poter 301 reindirizzamento da www.sub.example.coma sub.example.come quindi ottenere solo un certificato SSL per sub.example.comOra so che di controllo SSL cert accade prima del redirect 301, quindi non può funzionare. Maggiori spiegazioni qui: serverfault.com/a/358625/144811
Gruzzles

11

Questo funziona per me:

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
}

Tieni presente che entrambi yourdomain.com e www.yourdomain.com devono essere nel tuo certificato SSL. Ciò è possibile con un certificato jolly o con un nome alternativo del server, come spiegato qui . Controlla https://www.startssl.com per certificati validi e gratuiti che lo fanno. ( Edith : a partire dalla versione 56 di Chrome, i certificati di startsl non saranno più attendibili. Prova invece https://letsencrypt.org/ ).


Questo funziona davvero, ma ho pensato che potesse essere fatto in modo più chiaro senza molte linee di configurazione duplicate.
Zloynemec,

@zloynemec È possibile inserire roba SSL in un file .conf separato e utilizzare la includeregola per aggiungerla a entrambi i blocchi di server SSL.
Igettäjä,

Inoltre, se stai usando cloudflare devi pagare $ 10 / mese per poter reindirizzare e delegare i 2 sottodomini (www + qualcosa). Fammi sapere se esiste una soluzione alternativa.
Freedo,

7

Dopo aver trascorso così tanto tempo con centinaia di casi simili, ho escogitato il seguente frammento. È corto e può essere facilmente modificato per adattarsi a qualsiasi cosa.

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 ma ifè malvagio !

può essere. Ma esiste per un motivo e non dovrebbe nuocere a coloro che sanno come usarlo correttamente. ;)


Mi piace questo, ma hai qualche dato sul colpo di prestazione? Grazie!
Freedo,

1
Onestamente non l'ho mai confrontato, ma credo che non ci sarebbe quasi un impatto rispetto a regole separate poiché l'effetto è praticamente lo stesso.
emyller,

benchmark sul reindirizzamento? non è davvero pertinente no? (vera domanda, non un troll ^^)
Matrix,

3

Preferisco tornare con un codice di risposta in modo che il browser sappia che lo stai reindirizzando a un altro URL.

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

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

quindi un altro blocco di configurazioni del server per https

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

0

che ne dite di creare un blocco server per questo scopo:

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

quindi riavviare nginx


Viene visualizzato un errore "Nome server in conflitto" al riavvio. Inoltre, il comando non sarà in ascolto sulla porta 443 per SSL e ho bisogno di preoccuparsi di reindirizzamento https://www.example.coma https://example.compure.
Devin,

0

Penso che dovrebbe funzionare.

Sulla tua semplice definizione del server HTTP suggerito qualcosa come anthonysomerset, ovvero:

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

Quindi sulla definizione del tuo server SSL:

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

In questo modo il reindirizzamento dovrebbe avvenire solo una volta per richiesta, indipendentemente dall'URL a cui l'utente va originariamente.


Ha funzionato, grazie. Ho dovuto cambiare il tuo condizionale a if ($host = 'www.example.com') {poiché il tuo regex non funzionava per me, però. Non ho idea del perché, poiché sembra corretto.
Devin,

Nota che se è malvagio ed è generalmente meglio usare un modo dichiarativo.
Blaise,

0

Ecco l'esempio completo che ha finito per funzionare per me. Il problema era che non avevo i dettagli ssl ( ssl_certificate, ecc.) Nel blocco di reindirizzamento www. Ricordati di controllare i tuoi registri ( 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;
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.