web-dev-qa-db-fra.com

Supprimer "www" et rediriger vers "https" avec nginx

Je veux créer une règle dans nginx qui fait deux choses:

  1. Supprime le "www." de l'URI de la demande
  2. Redirige vers "https" si l'URI de la demande est "http"

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?

60
Devin

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

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

11
e18r

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. ;)

7
emyller

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;
        ...
    }
3
montss

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

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

0
anthonysomerset

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.

0
Eduardo Ivanec