Xóa phần mềm www và chuyển hướng đến bản https https bằng nginx


57

Tôi muốn tạo một quy tắc trong nginx có hai điều:

  1. Xóa "www." từ URI yêu cầu
  2. Chuyển hướng đến "https" nếu URI yêu cầu là "http"

Có rất nhiều ví dụ về cách thực hiện từng điều đó một cách riêng lẻ, nhưng tôi không thể tìm ra một giải pháp thực hiện chính xác cả hai (nghĩa là không tạo ra một vòng lặp chuyển hướng và xử lý tất cả các trường hợp đúng cách).

Nó cần phải xử lý tất cả các trường hợp sau:

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

Tất cả những thứ này sẽ kết thúc tại https://example.com/path (# 4) mà không cần lặp. Có ý kiến ​​gì không?


Tôi vừa chuyển hướng www.mydomain.com sang mydomain.com ở cấp DNS và thêm 301 cho không phải https thành https trong nginx. Có vẻ như vậy sẽ ổn thôi ¯ \ _ () _ /
jonathanbell

Câu trả lời:


94

Cách tốt nhất để thực hiện việc này là sử dụng ba khối máy chủ: một để chuyển hướng http sang https, một để chuyển hướng tên www https sang no-www và một để thực sự xử lý các yêu cầu. Lý do sử dụng các khối máy chủ bổ sung thay vì ifs là việc lựa chọn máy chủ được thực hiện bằng bảng băm và rất nhanh. Sử dụng cấp độ máy chủ nếu có nghĩa là if được chạy cho mọi yêu cầu, điều này thật lãng phí. Ngoài ra, việc bắt các uri được yêu cầu trong phần viết lại là lãng phí, vì nginx đã có thông tin này trong các biến $ uri và $ request_uri (không có và có chuỗi truy vấn, tương ứng).

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
Là khối giữa cần thiết? Không phải khối đầu tiên đã được viết lại từ www thành không phải www?
poustitenbach

3
Khối đầu tiên chỉ xử lý http. Khối giữa là cần thiết để chuyển hướng các yêu cầu https từ https: // www.example.com/ sang https: // example.com/. (Xin lỗi vì có thêm không gian, tôi không thể làm cho nó hiển thị https nếu không)
kolbyjack

1
chỉ là một ghi chú định dạng nhỏ - nếu bạn muốn tránh tạo liên kết, bạn có thể đặt văn bản nhận xét bên trong dấu ngoặc kép `, cái dưới dấu ngã. Nó sẽ hiển thị như sau:https://example.com/
Cyclops

9
khối thứ hai cũng cần thông tin chứng chỉ.
ricka

3
Thử câu trả lời này, tôi gặp phải một vấn đề khác. Nghĩ rằng tôi có thể chuyển hướng 301 từ www.sub.example.comđến sub.example.comvà sau đó chỉ nhận được chứng chỉ SSL cho sub.example.comBây giờ tôi biết rằng kiểm tra chứng chỉ ssl xảy ra trước khi chuyển hướng 301, vì vậy nó không thể hoạt động. Giải thích thêm tại đây: serverfault.com/a3538625/144811
Gru phun

11

Điều này làm việc cho tôi:

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
}

Hãy nhớ rằng cả hai yourdomain.com www.yourdomain.com phải có trong chứng chỉ SSL của bạn. Điều này có thể với chứng chỉ ký tự đại diện hoặc với Tên thay thế máy chủ như được giải thích ở đây . Kiểm tra https://www.startssl.com để biết các chứng chỉ đẹp và miễn phí thực hiện việc này. ( Edith : bắt đầu với Chrome phiên bản 56, chứng chỉ bắt đầu sẽ không được tin cậy nữa. Thay vào đó, hãy thử https://letsencrypt.org/ .)


Điều này thực sự hoạt động, nhưng tôi nghĩ rằng nó có thể được thực hiện theo cách rõ ràng hơn mà không có nhiều dòng cấu hình trùng lặp.
zloynemec

@zloynemec Bạn có thể đặt nội dung SSL vào một tệp .conf riêng biệt và sử dụng includequy tắc để thêm nó vào cả hai khối máy chủ SSL.
Igettäjä

Ngoài ra nếu bạn đang sử dụng cloudflare, bạn cần trả chứng chỉ $ 10 / tháng để có thể chuyển hướng và ủy quyền cho 2 tên miền phụ (www + gì đó). Hãy cho tôi biết nếu có một cách giải quyết.
Freedo

7

Sau khi dành quá nhiều thời gian với hàng trăm trường hợp tương tự, tôi đã nghĩ ra đoạn trích sau. Nó ngắn và có thể dễ dàng điều chỉnh để phù hợp với bất cứ điều gì.

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 / {
    # ...
}

Ôi nhưng iflà ác !

Vâng, nó thể. Nhưng nó tồn tại vì một lý do, và không gây hại cho những người biết sử dụng nó đúng cách. ;)


Tôi thích điều này, nhưng bạn có dữ liệu nào về hiệu suất không? Cảm ơn bạn!
Freedo

1
Thành thật mà nói tôi không bao giờ đánh giá điều đó, nhưng tôi tin rằng sẽ khó có tác động so với các quy tắc riêng biệt vì hiệu ứng này khá giống nhau.
emyller

điểm chuẩn về chuyển hướng? Nó không thực sự thích hợp phải không? (câu hỏi thực sự, không phải là một kẻ troll ^^)
Ma trận

3

Tôi thích quay lại với mã phản hồi để trình duyệt biết bạn đang chuyển hướng nó đến một URL khác.

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

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

sau đó một khối cấu hình máy chủ khác cho https

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

0

làm thế nào về việc tạo một khối máy chủ cho mục đích này:

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

sau đó khởi động lại nginx


Tôi gặp lỗi "tên máy chủ xung đột" khi khởi động lại. Ngoài ra, lệnh đó sẽ không nghe trên cổng 443 cho SSL và tôi cũng cần lo lắng về việc chuyển hướng https://www.example.comđến https://example.com.
Devin

0

Tôi nghĩ rằng điều này sẽ làm việc.

Trên định nghĩa máy chủ HTTP đơn giản của bạn, một cái gì đó như anthonysomerset đã đề xuất, đó là:

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

Sau đó, trên định nghĩa máy chủ SSL của bạn:

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

Bằng cách này, việc chuyển hướng chỉ nên xảy ra một lần cho mỗi yêu cầu bất kể người dùng truy cập URL nào ban đầu.


Điều đó đã làm việc, cảm ơn. Tôi đã phải thay đổi điều kiện của bạn thành if ($host = 'www.example.com') {vì regex của bạn không làm việc cho tôi, mặc dù. Không biết tại sao, vì nó có vẻ chính xác.
Devin

Xin lưu ý rằng nếu là xấu xa và nói chung là tốt hơn để sử dụng một cách khai báo.
Blaise

0

Đây là ví dụ đầy đủ đã làm việc cho tôi. Vấn đề là tôi không có các chi tiết ssl ( ssl_certificate, v.v.) trong khối chuyển hướng www. Hãy nhớ kiểm tra nhật ký của bạn ( 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;
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.