In Nginx, come posso riscrivere tutte le richieste http su https mantenendo il sottodominio?


509

Voglio riscrivere tutte le richieste http sul mio server Web come richieste https, ho iniziato con il seguente:

server {
    ascolta 80;

    Posizione / {
      riscrivi ^ (. *) https: //mysite.com$1 permanente;
    }
...


Un problema è che questo elimina qualsiasi informazione del sottodominio (ad esempio, node1.mysite.com/folder), come potrei riscrivere quanto sopra per reindirizzare tutto su https e mantenere il sottodominio?


2
Considerare di spostare la "risposta accettata" su serverfault.com/a/171238/90758 . Questo è quello corretto.
olafure del

Basta usare $ nome_server invece di mysite.com hardcoded
Fedir RYKHTIK

Risposte:


749

Modo corretto nelle nuove versioni di nginx

Ho scoperto che la mia prima risposta a questa domanda era corretta in un determinato momento, ma si è trasformata in un'altra trappola - per rimanere aggiornato, controlla le tasse e riscrivi le insidie

Sono stato corretto da molti utenti SE, quindi il merito va a loro, ma soprattutto, ecco il codice corretto:

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

server {
       listen         443 ssl;
       server_name    my.domain.com;
       # add Strict-Transport-Security to prevent man in the middle attacks
       add_header Strict-Transport-Security "max-age=31536000" always; 

       [....]
}

3
Dovresti comunque farlo su un dominio per dominio - no? E se volessi applicarlo a tutti i domini sul tuo server?
JM4,

30
@ JM4: se usi $ host $ nella riscrittura invece di nome_server e aggiungi default_server alla direttiva hear funzionerà per ogni dominio sul tuo server.
Klaas van Schelven,

5
È importante ricordare che il 301 è archiviato nella cache locale senza data di scadenza. Non molto utile quando cambia la configurazione
Trefex

9
@everyone Utilizzare un reindirizzamento 307 per conservare il contenuto POST.
Mahmoud Al-Qudsi,

10
Si noti che è necessario utilizzare $hostinvece che $server_namese si utilizzano sottodomini.
Pesce gatto

276

NOTA: il modo migliore per farlo è stato fornito da https://serverfault.com/a/401632/3641 - ma è ripetuto qui:

server {
    listen         80;
    return 301 https://$host$request_uri;
}

Nel caso più semplice il tuo host verrà riparato come il tuo servizio al quale desideri inviarlo - questo farà reindirizzare 301 al browser e l'URL del browser verrà aggiornato di conseguenza.

Di seguito è la risposta precedente, che è inefficiente a causa di regex, un semplice 301 è fantastico, come mostrato da @kmindi

Ho usato nginx 0.8.39 e versioni successive e ho usato quanto segue:

 server {
       listen 80;
       rewrite ^(.*) https://$host$1 permanent;
 }

Invia un reindirizzamento permanente al client.


15
Penso che dovrebbe essere 80, poiché questo è in ascolto di http e quindi dice al client di tornare come https (443).
Michael Neale,

3
Questa dovrebbe essere la risposta migliore!
Nathan,

3
Questa è la risposta più faticosa.
Caso

1
questo è il più semplice, ma il meno sicuro: in questo modo consenti al tuo server di reindirizzare un utente a qualsiasi pagina, senza verificare se è consentito l'utilizzo sul tuo server. Se il tuo server serve mydomain.co, gli utenti malintenzionati potrebbero comunque utilizzare il tuo server per reindirizzare gli utenti ad altri domini come mydomain.co, come google.com.
friedkiwi,

10
@ cab0lt non ci sono problemi di sicurezza qui. Fornire un reindirizzamento non presenta rischi per la sicurezza. Se esistono requisiti di controllo dell'accesso, questi dovrebbero essere verificati nel punto in cui il browser richiede il nuovo URL. Il browser non accederà semplicemente sulla base del reindirizzamento, né avrà bisogno del reindirizzamento per richiedere il nuovo URL.
mc0e

125

Penso che il modo migliore e unico debba essere un reindirizzamento permanente spostato HTTP 301 in questo modo:

server {
    listen         [::]:80;
    return 301 https://$host$request_uri;
}

Il reindirizzamento permanente spostato HTTP 301 è anche il più efficiente perché non esiste alcun regex da valutare, in base a errori già menzionati .


Il nuovo HTTP 308 spostato mantiene permanentemente il metodo Request ed è supportato dai principali browser . Ad esempio, l'utilizzo 308impedisce ai browser di modificare il metodo di richiesta da POSTa GETper la richiesta di reindirizzamento.


Se vuoi preservare il nome host e il sottodominio, questo è il modo.

Questo fa ancora funzionare se non si dispone di DNS , come Sono anche utilizzando localmente. Sto richiedendo ad esempio con http://192.168.0.100/index.phpe verrai reindirizzato esattamente https://192.168.0.100/index.php.

Uso listen [::]:80sul mio host perché ho bindv6onlyimpostato su false, quindi si lega anche al socket ipv4. cambiarlo in listen 80se non si desidera IPv6 o si desidera associare altrove.

La soluzione di Saif Bechan utilizza quella server_nameche nel mio caso è localhost ma che non è raggiungibile tramite una rete.

La soluzione di Michael Neale è buona, ma secondo le insidie, c'è una soluzione migliore con il reindirizzamento 301;)


Bello, provi a citarlo, ma 301 non funziona su HTTPS.
Caso

5
cosa non funziona? la sezione del server indicata è per il traffico http (senza) non crittografato da reindirizzare permanentemente al server crittografato (quella sezione che è in ascolto su 443 (https) non è elencata)
kmindi

Ho controllato che funzioni alla grande con https e tutto il resto - @kmindi ho aggiornato la mia risposta con riferimento alla tua - poiché penso che sia la strada giusta e questo continua a spuntare! Bel lavoro.
Michael Neale,

Quando si utilizza una richiesta di dominio (non IP), non funziona se non si modifica "[::]: 80" in "80".
Joseph Lust,

quello potrebbe essere il comportamento previsto: trac.nginx.org/nginx/ticket/345 . Ho aggiornato la risposta per descrivere l'opzione di ascolto.
kmindi,

20

All'interno del blocco server è inoltre possibile effettuare le seguenti operazioni:

# Force HTTPS connection. This rules is domain agnostic
if ($scheme != "https") {
    rewrite ^ https://$host$uri permanent;
}

2
Questa configurazione ha causato il mio server per produrre un ciclo di reindirizzamento
Corkscreewe

Forse perché c'è un altro reindirizzamento in atto o https non è abilitato nel tuo sito / app
Oriol

1
Nessuno degli altri sembrava funzionare tranne questo. Utilizzando la versione di nginx: nginx / 1.10.0 (Ubuntu)
ThatGuy343

votato per https: // $ host $ uri
AMB

4
Questa è la strada da percorrere se sei dietro un bilanciamento del carico!
Antwan,

17

Quanto sopra non ha funzionato con la creazione continua di nuovi sottodomini. ad es. AAA.example.com BBB.example.com per circa 30 sottodomini.

Finalmente ho ottenuto una configurazione che funziona con il seguente:

server {
  listen 80;
  server_name _;
  rewrite ^ https://$host$request_uri? permanent;
}
server {
  listen  443;
  server_name example.com;
  ssl on;
  ssl_certificate /etc/ssl/certs/myssl.crt;
  ssl_certificate_key /etc/ssl/private/myssl.key;
  ssl_prefer_server_ciphers       on;
# ...
# rest of config here
# ...
}

grazie! nginx restituisce 301 https://*/o annulla prematuramente la richiesta nelle altre risposte qui. server_name _;con $hostera la risposta che ha fatto il trucco. +1
zamnuts

1
Questo è ottimale! Tuttavia, consiglio ad alcuni di sostituire _il dominio effettivo, ad esempio .domain.comavevo due server e nginx indirizzava accidentalmente uno dei miei server sul server predefinito.
zzz,

1
Questa è l'unica risposta che ha funzionato per me, grazie!
Pupazzo di neve,

Grazie mille amico .. Ho provato molte soluzioni ma non ho funzionato. Questa soluzione è fantastica e ha funzionato per me. nome del server _; cosa significa questo .. non ho capito. Per favore, spiegamelo.
Pavan Kumar,

6

Ho pubblicato un commento sulla risposta corretta molto, molto tempo fa con una correzione molto importante, ma sento che è necessario evidenziare questa correzione nella sua risposta. Nessuna delle risposte precedenti è sicura da utilizzare se in qualsiasi momento hai impostato HTTP non sicuro e ti aspettavi il contenuto dell'utente, disponi di moduli, ospita un'API o hai configurato qualsiasi sito Web, strumento, applicazione o utilità per parlare al tuo sito.

Il problema si verifica quando POSTviene effettuata una richiesta al server. Se la risposta del server con un semplice 30xreindirizzamento, il contenuto POST andrà perso. Quello che succede è che il browser / client aggiornare la richiesta di SSL ma declassare la POSTa una GETrichiesta. I POSTparametri andranno persi e verrà inviata una richiesta errata al server.

La soluzione è semplice Devi utilizzare un HTTP 1.1 307reindirizzamento. Questo è dettagliato in RFC 7231 S6.4.7:

  Note: This status code is similar to 302 (Found), except that it
  does not allow changing the request method from POST to GET.  This
  specification defines no equivalent counterpart for 301 (Moved
  Permanently) ([RFC7238], however, defines the status code 308
  (Permanent Redirect) for this purpose).

La soluzione, adattata dalla soluzione accettata, è utilizzare 307nel codice di reindirizzamento:

server {
       listen         80;
       server_name    my.domain.com;
       return         307 https://$server_name$request_uri;
}

server {
       listen         443 ssl;
       server_name    my.domain.com;
       # add Strict-Transport-Security to prevent man in the middle attacks
       add_header Strict-Transport-Security "max-age=31536000"; 

       [....]
}


4

Sto correndo ngnix dietro un ELS AWS. L'ELB sta parlando con ngnix su http. Poiché ELB non ha modo di inviare reindirizzamenti ai client, controllo l'intestazione X-Forwarded-Proto e reindirizzamento:

if ($http_x_forwarded_proto != 'https') {
    return 301 "https://www.exampl.com";
}

1

Se return 301 https://$host$request_uri;come risposta predefinita sulla porta 80, il tuo server potrebbe prima o poi ottenere un elenco di proxy aperti [1] e iniziare ad essere abusato di inviare traffico altrove su Internet. Se i tuoi registri si riempiono di messaggi come questo, allora sai che ti è successo:

42.232.104.114 - - [25/Mar/2018:04:50:49 +0000] "GET http://www.ioffer.com/i/new-fashion-fine-gold-bracelet-versaec-bracelet-641175733 HTTP/1.1" 301 185 "http://www.ioffer.com/" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; Hotbar 4.1.8.0; RogueCleaner; Alexa Toolbar)"

Il problema è che $hosttornerà indietro qualunque cosa il browser invii Hostnell'intestazione o anche il nome host dalla riga iniziale di HTTP, come questo:

GET http://www.ioffer.com/i/new-fashion-fine-gold-bracelet-versaec-bracelet-641175733 HTTP/1.1

A causa di questo problema, alcune altre risposte qui raccomandano di usare $server_nameinvece di $host. $server_namevaluta sempre ciò che hai inserito nella server_namedichiarazione. Ma se hai più sottodomini lì o usi un carattere jolly, questo non funzionerà, perché $server_nameusa solo la prima voce dopo la server_namedichiarazione e, cosa più importante, tornerà indietro un carattere jolly (non espanderlo).

Quindi, come supportare più domini mantenendo la sicurezza? Sui miei sistemi ho affrontato questo dilemma elencando prima un default_serverblocco che non utilizza $host, quindi elencando un blocco jolly che fa:

server {
  listen 80 default_server;
  server_name example.com;
  return 301 https://example.com$request_uri;
}
server {
  listen 80;
  server_name *.example.com;
  return 301 https://$host$request_uri;
}

(Potresti anche elencare più di un dominio nel secondo blocco.)

Con questa combinazione, i domini senza pari verranno reindirizzati da qualche parte (sempre example.com) hardcoded e i domini che corrispondono ai tuoi andranno nel posto giusto. Il tuo server non sarà utile come proxy aperto, quindi non attirerai problemi.

Se ti senti strano, suppongo che potresti anche fare in modo che il default_serverblocco non corrisponda a nessuno dei tuoi domini legittimi e offra qualcosa di offensivo. . . .

[1] Tecnicamente "proxy" è la parola sbagliata, perché il tuo server non esce e non soddisfa le richieste per i client, sta solo inviando un reindirizzamento, ma non sono sicuro di quale sia la parola giusta. Inoltre, non sono sicuro di quale sia l'obiettivo, ma riempie i log di rumore e consuma la CPU e la larghezza di banda, quindi potresti anche fermarlo.


0

Sembra che nessuno abbia capito bene al 100%. Per fare in modo che le richieste della porta 80 vadano ai loro 443 equivalenti per un intero server Web, è necessario utilizzare la direttiva Listen , non la direttiva nome_server per specificare il nome generale. Vedi anche https://nginx.org/en/docs/http/request_processing.html

server {
    ascolta 80 predefinito;
    ascolta [::]: 80 predefinito;
      return 307 https: // $ host $ request_uri;
}
  • $ host rileva i nomi dei sottodomini.
  • 307 e 308 includono entrambi gli URI di richiesta POST e GET.
  • 307 è temporaneo, passare al permanente 308 dopo test approfonditi:

E assicurati di controllare cosa c'è già in /etc/nginx/conf.d/ perché il più delle volte ho avuto problemi in cui default.conf ha restituito un vhost esistente. Il mio ordine di lavorare con i problemi di nginx inizia sempre spostando il file predefinito, rimettendolo indietro commentando riga per riga per vedere dove va storto.


-1
rewrite ^!https https://$host$request_uri permanent;
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.