Je veux créer une règle dans nginx qui fait deux choses:
Il existe de nombreux exemples de la façon de faire chacune de ces choses individuellement, mais je ne peux pas trouver une solution qui fait les deux correctement (c'est-à-dire qui ne crée pas de boucle de redirection et gère correctement tous les cas).
Il doit gérer tous ces cas:
1. http://www.example.com/path
2. https://www.example.com/path
3. http://example.com/path
4. https://example.com/path
Ceux-ci devraient tous se retrouver à https://example.com/path (# 4) sans boucle. Des idées?
La meilleure façon d'y parvenir est d'utiliser trois blocs de serveur: un pour rediriger http vers https, un pour rediriger le https www-nom vers no-www, et un pour gérer réellement les requêtes. La raison d'utiliser des blocs de serveur supplémentaires au lieu de ifs est que la sélection du serveur est effectuée à l'aide d'une table de hachage et est très rapide. L'utilisation d'un if au niveau du serveur signifie que le if est exécuté pour chaque demande, ce qui est inutile. En outre, la capture de l'uri demandé dans la réécriture est un gaspillage, car nginx a déjà ces informations dans les variables $ uri et $ request_uri (respectivement sans et avec la chaîne de requête).
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>
}
Cela fonctionne pour moi:
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
}
Gardez à l'esprit que les deuxyourdomain.com
etwww.yourdomain.com
doit être dans votre certificat SSL. Ceci est possible avec un certificat générique ou avec un autre nom de serveur comme expliqué ici . Vérifiez https://www.startssl.com pour les certificats Nice et gratuits qui le font. ( Edith : à partir de Chrome version 56, les certificats startssl ne seront plus fiables. Essayez https : //letsencrypt.org/ à la place.)
Après avoir passé tant de temps avec des centaines de cas similaires, j'ai trouvé l'extrait de code suivant. Il est court et peut être facilement modifié pour s'adapter à tout.
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 mais
if
est mauvais !
Oui, cela peut être. Mais il existe pour une raison et ne devrait pas nuire à ceux qui savent l'utiliser correctement. ;)
Je préfère retourner avec un code de réponse pour que le navigateur sache que vous le redirigez vers une autre URL.
server {
listen 80;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
puis un autre bloc de configurations de serveur pour le https
server {
listen 443 ssl;
server_name example.com;
...
}
Voici l'exemple complet qui a fini par fonctionner pour moi. Le problème était que je n'avais pas les détails SSL (ssl_certificate
, etc.) dans le bloc de redirection www. N'oubliez pas de vérifier vos journaux (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;
}
}
que diriez-vous de créer un bloc serveur à cet effet:
server{
listen 80;
server_name www.example.net example.net;
rewrite ^(.*) https://example.net$1 permanent;
}
puis redémarrage de nginx
Je pense que cela devrait fonctionner.
Dans votre définition de serveur HTTP ordinaire, quelque chose comme anthonysomerset a suggéré, c'est-à-dire:
rewrite ^(.*) https://example.net$1 permanent;
Puis sur votre définition de serveur SSL:
if ($Host ~ /^www\./) {
rewrite ^(.*) https://example.net$1 permanent;
}
De cette façon, la redirection ne devrait se produire qu'une seule fois par demande, quelle que soit l'URL vers laquelle l'utilisateur accède à l'origine.