Dans Nginx, comment puis-je réécrire toutes les demandes http en https tout en maintenant le sous-domaine?

509

Je souhaite réécrire toutes les requêtes http sur mon serveur Web en requêtes https. J'ai commencé avec les éléments suivants:

serveur {
    écoutez 80;

    emplacement / {
      réécrire ^ (. *) https: //monsite.com$1 permanent;
    }
...


Un problème est que cela supprime toute information de sous-domaine (par exemple, node1.mysite.com/folder), comment pourrais-je réécrire ce qui précède pour tout rediriger vers https et maintenir le sous-domaine?

MikeN
la source
2
Envisagez de déplacer la "réponse acceptée" vers serverfault.com/a/171238/90758 . C'est le bon.
Olafure
Il suffit d'utiliser $ server_name au lieu de hardcoded mysite.com
Fedir RYKHTIK

Réponses:

749

Manière correcte dans les nouvelles versions de nginx

Ma première réponse à cette question était correcte à un moment donné, mais elle s'est transformée en un autre piège - pour rester à jour, merci de vérifier Taxer les pièges de réécriture

J'ai été corrigé par de nombreux utilisateurs SE, le mérite leur revient donc, mais plus important encore, voici le code correct:

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; 

       [....]
}
Saif Bechan
la source
3
Cependant, vous devrez le faire domaine par domaine - non? Et si vous vouliez l'appliquer à chaque domaine de votre serveur?
JM4
30
@ JM4: si vous utilisez $ host $ dans la réécriture au lieu de nom_serveur et ajoutez default_server à la directive listen, cela fonctionnera pour tous les domaines de votre serveur.
Klaas van Schelven
5
Il est important de mentionner que le 301 est stocké dans votre cache local sans date d'expiration. Pas très utile lorsque la configuration change.
Trefex
9
@everyone Utilisez une redirection 307 pour conserver le contenu POST.
Mahmoud Al-Qudsi,
10
Notez que vous devez utiliser $hostau lieu de $server_namesi vous utilisez des sous-domaines.
Poisson
276

REMARQUE: La meilleure façon de procéder est fournie par https://serverfault.com/a/401632/3641 - mais elle est répétée ici:

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

Dans le cas le plus simple, votre hôte sera désigné comme étant le service auquel vous souhaitez les envoyer - une redirection 301 vers le navigateur sera effectuée et l'URL du navigateur sera mise à jour en conséquence.

Voici la réponse précédente, qui est inefficace à cause de regex, un simple 301 est génial, comme le montre @kmindi

J'utilise nginx 0.8.39 et les versions ultérieures, et j'utilise les éléments suivants:

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

Envoie une redirection permanente au client.

Michael Neale
la source
15
Je pense que cela devrait être 80 - car ceci écoute http et demande ensuite au client de revenir en tant que https (443).
Michael Neale
3
Cela devrait être la meilleure réponse!
Nathan
3
C'est la réponse la plus éprouvante.
Affaire
1
C’est la solution la plus simple, mais la moins sécurisée. Ainsi, vous autorisez votre serveur à rediriger un utilisateur vers n’importe quelle page, sans vérifier si son utilisation est autorisée. Si votre serveur dessert mydomain.co, des utilisateurs malveillants peuvent toujours utiliser votre serveur pour les rediriger vers d'autres domaines, tels que mydomain.co, tels que google.com.
friedkiwi
10
@ cab0lt il n'y a pas de problème de sécurité ici. Servir une redirection ne présente pas de risque de sécurité. S'il existe des exigences de contrôle d'accès, celles-ci doivent être vérifiées au moment où le navigateur demande la nouvelle URL. Le navigateur n'obtiendra pas l'accès simplement sur la base de la redirection, pas plus qu'il n'aura besoin de la redirection pour demander la nouvelle URL.
mc0e
125

Je pense que le meilleur et le seul moyen devrait être d'utiliser un HTTP 301 déplacé de façon permanente, comme suit:

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

La redirection HTTP 301 Moved Permanently est également la plus efficace, car il n’ya pas d’expression rationnelle à évaluer, selon les pièges déjà mentionnés .


Le nouveau protocole HTTP 308 déplacé conserve de manière permanente la méthode Request et est pris en charge par les principaux navigateurs . Par exemple, l'utilisation 308empêche les navigateurs de changer la méthode de demande de POSTen GETpour la demande de redirection.


Si vous souhaitez conserver le nom d’hôte et le sous - domaine, c’est la solution.

Cela fonctionne toujours si vous n’avez pas de DNS , car je l’utilise aussi localement. Je demande par exemple avec http://192.168.0.100/index.phpet sera redirigé vers exactement https://192.168.0.100/index.php.

J'utilise listen [::]:80sur mon hôte parce que je l'ai bindv6onlydéfini sur false, de sorte qu'il se lie également à la prise ipv4. changez-le en listen 80si vous ne voulez pas IPv6 ou si vous voulez vous connecter ailleurs.

La solution de Saif Bechan utilise le server_namequi dans mon cas est localhost mais qui n'est pas accessible via un réseau.

La solution de Michael Neale est bonne, mais selon les pièges, il existe une meilleure solution avec redirect 301;)

kmindi
la source
Bien que vous essayiez de le citer, mais 301 ne fonctionne pas sur HTTPS.
Affaire
5
qu'est-ce qui ne marche pas? la section serveur indiquée concerne le trafic HTTP (sans s) non crypté (e) s pour être redirigé (e) de manière permanente vers le serveur crypté (cette section qui écoute sur 443 (https) n'est pas répertoriée)
kmindi
J'ai vérifié que cela fonctionnait bien avec https et tout le reste - @kmindi, j'ai mis à jour ma réponse en faisant référence à la vôtre - car je pense que c'est la bonne manière et que cela continue à apparaître! Bon travail.
Michael Neale
Lors de l'utilisation d'une demande de domaine (non-ip), ne fonctionne que si je modifie '[::]: 80' en '80'.
Joseph Lust
cela pourrait être le comportement attendu: trac.nginx.org/nginx/ticket/345 . J'ai mis à jour la réponse pour décrire l'option d'écoute.
kmindi
20

Dans le bloc serveur, vous pouvez également effectuer les opérations suivantes:

# Force HTTPS connection. This rules is domain agnostic
if ($scheme != "https") {
    rewrite ^ https://$host$uri permanent;
}
Oriol
la source
2
Cette configuration a provoqué une boucle de redirection sur mon serveur
Corkscreewe
Peut-être parce qu’une autre redirection est en place ou que https n’est pas activé sur votre site / votre application
Oriol
1
Aucun des autres ne semblait fonctionner sauf celui-ci. Utilisation de nginx version: nginx / 1.10.0 (Ubuntu)
ThatGuy343
voté pour https: // $ hôte $ uri
AMB
4
C'est la voie à suivre si vous êtes derrière un loadbalancer!
Antwan
17

Ce qui précède n'a pas fonctionné avec de nouveaux sous-domaines étant créés tout le temps. Exemple: AAA.example.com BBB.example.com pour environ 30 sous-domaines.

Enfin, une configuration fonctionnant avec les éléments suivants:

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
# ...
}
Aleck Landgraf
la source
Merci! nginx renverrait 301 https://*/ou annulerait la demande prématurément dans les autres réponses ici. server_name _;avec $hostétait la réponse qui a fait le tour. +1
zamnuts
1
Celui-ci est optimal! Cependant, je recommande à certains de les remplacer _par le domaine réel, par exemple, .domain.comj'avais deux serveurs, et nginx dirigeait accidentellement l'un de mes serveurs vers le serveur par défaut.
zzz
1
C'est la seule réponse qui a fonctionné pour moi, merci!
Bonhomme de neige
Merci beaucoup mon pote .. J'ai essayé beaucoup de solutions mais je n'ai pas trouvé de solution. Cette solution est géniale et a fonctionné pour moi. nom du serveur _; Qu'est-ce que cela signifie pour .. Je n'ai pas compris. S'il vous plaît, expliquez-moi ceci.
Pavan Kumar
6

J'ai posté un commentaire sur la bonne réponse il y a très longtemps avec une correction très importante, mais j'estime qu'il est nécessaire de mettre en évidence cette correction dans sa propre réponse. Aucune des réponses précédentes ne peut être utilisée en toute sécurité si, à un moment quelconque, vous avez configuré HTTP non sécurisé et attendez-vous du contenu utilisateur, des formulaires, l'hôte d'une API ou avez configuré un site Web, un outil, une application ou un utilitaire pour communiquer avec votre site.

Le problème se produit lorsqu'une POSTdemande est faite à votre serveur. Si la réponse du serveur avec une 30xredirection en clair, le contenu du POST sera perdu. Qu'est-ce qui se passe est que le navigateur / client mettra à niveau la demande à SSL mais rétrograder la demande POSTà une GETdemande. Les POSTparamètres seront perdus et une demande incorrecte sera faite à votre serveur.

La solution est simple Vous devez utiliser une HTTP 1.1 307redirection. Ceci est détaillé dans 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 solution, adaptée de la solution acceptée, consiste à utiliser 307dans votre code de redirection:

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

       [....]
}
Mahmoud Al-Qudsi
la source
4

J'ai réussi à le faire comme ça:

server {
listen 80;
listen 443 ssl;

server_name domain.tld www.domain.tld;

# global HTTP handler
if ($scheme = http) {
        return 301 https://www.domain.tld$request_uri;
}

# global non-WWW HTTPS handler
if ($http_host = domain.tld){
        return 303 https://www.domain.tld$request_uri;
}
}

https://stackoverflow.com/a/36777526/6076984

stamster
la source
Version mise à jour, sans IF: paste.debian.net/plain/899679
stamster
4

Je lance ngnix derrière un AWS ELB. L'ELB parle à ngnix via http. Etant donné que l'ELB n'a aucun moyen d'envoyer des redirections aux clients, je vérifie l'en-tête X-Forwarded-Proto et redirige:

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

Si vous return 301 https://$host$request_uri;répondez par défaut sur le port 80, votre serveur pourrait tôt ou tard figurer sur une liste de proxies ouverts [1] et commencer à être abusé pour envoyer du trafic ailleurs sur Internet. Si vos journaux se remplissent de messages comme celui-ci, alors vous savez que cela vous est arrivé:

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)"

Le problème est que cela $hostfera écho à tout ce que le navigateur envoie dans l'en- Hosttête ou même le nom d'hôte à partir de la première ligne de HTTP, comme celle-ci:

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

À cause de ce problème, certaines autres réponses recommandent d'utiliser $server_nameplutôt que de $host. $server_nameévalue toujours à ce que vous avez mis dans la server_namedéclaration. Mais si vous avez plusieurs sous-domaines ou si vous utilisez un caractère générique, cela ne fonctionnera pas, car $server_namen'utilise que la première entrée après la server_namedéclaration et, plus important encore, fera simplement écho à un caractère générique (pas à le développer).

Alors, comment prendre en charge plusieurs domaines tout en maintenant la sécurité? Sur mes propres systèmes, j'ai résolu ce dilemme en listant tout d' abord un default_serverbloc non utilisé $host, puis en listant un bloc générique qui:

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

(Vous pouvez également répertorier plus d'un domaine dans le deuxième bloc.)

Avec cette combinaison, les domaines sans correspondance seront redirigés quelque part (toujours example.com) codés en dur , et les domaines correspondant aux vôtres iront au bon endroit. Votre serveur ne sera pas utile en tant que proxy ouvert, vous ne rencontrerez donc aucun problème.

Si vous vous sentez vexé, je suppose que vous pourriez également faire en sorte que le default_serverbloc ne corresponde à aucun de vos domaines légitimes et serve quelque chose d'offensant. . . .

[1] Techniquement, "proxy" n'est pas le bon mot, car votre serveur ne répond pas et ne répond pas aux demandes des clients, mais envoie simplement une redirection, mais je ne suis pas sûr du mot exact. Je ne sais pas non plus quel est l'objectif, mais il remplit vos journaux de bruit et consomme votre processeur et votre bande passante. Vous pouvez donc tout aussi bien y mettre un terme.

Paul A Jungwirth
la source
0

On dirait que personne ne l'a vraiment compris à 100%. Pour que les demandes du port 80 atteignent leurs équivalents 443 pour un serveur Web entier, vous devez utiliser la directive listen , et non la directive server_name, pour spécifier le nom fourre-tout . Voir aussi https://nginx.org/en/docs/http/request_processing.html

serveur {
    écouter 80 par défaut;
    listen [::]: 80 default;
      return 307 https: // $ host $ request_uri;
}
  • $ host capture les noms de sous-domaines.
  • 307 et 308 incluent les adresses URI des demandes POST et GET.
  • 307 est temporaire, modification du permanent 308 après des tests approfondis:

Et assurez-vous de vérifier ce qui est déjà dans /etc/nginx/conf.d/ car, le plus souvent, je rencontrais des problèmes dans lesquels le fichier default.conf renvoyait des vhost existants. Mon ordre de travail avec les problèmes nginx commence toujours par déplacer le fichier par défaut, en le remettant en commentaire ligne par ligne pour voir où il ne va pas.

Julius
la source
-1
rewrite ^!https https://$host$request_uri permanent;
nntb2a
la source