Trong Nginx, làm cách nào tôi có thể viết lại tất cả các yêu cầu http thành https trong khi duy trì tên miền phụ?


508

Tôi muốn viết lại tất cả các yêu cầu http trên máy chủ web của mình thành các yêu cầu https, tôi đã bắt đầu với các điều sau:

người phục vụ {
    nghe 80;

    vị trí / {
      viết lại ^ (. *) https: //mysite.com$1 vĩnh viễn;
    }
...


Một vấn đề là điều này loại bỏ bất kỳ thông tin tên miền phụ nào (ví dụ: node1.mysite.com/folder), làm thế nào tôi có thể viết lại phần trên để định tuyến lại mọi thứ thành https và duy trì tên miền phụ?


2
Vui lòng xem xét việc chuyển 'câu trả lời được chấp nhận' sang serverfault.com/a/171238/90758 . Đó là một trong những chính xác.
olafure

Chỉ cần sử dụng $ server_name thay vì mysite.com được mã hóa cứng
Fedir RYKHTIK

Câu trả lời:


748

Cách chính xác trong các phiên bản mới của nginx

Hóa ra câu trả lời đầu tiên của tôi cho câu hỏi này là đúng vào một thời điểm nhất định, nhưng nó lại biến thành một cạm bẫy khác - để luôn cập nhật, vui lòng kiểm tra các cạm bẫy viết lại Thuế

Tôi đã được sửa chữa bởi nhiều người dùng SE, vì vậy tín dụng dành cho họ, nhưng quan trọng hơn, đây là mã chính xác:

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
Tuy nhiên, bạn sẽ phải làm điều này trên một tên miền theo tên miền - không? Điều gì nếu bạn muốn áp dụng nó cho mọi miền trên máy chủ của bạn?
JM4

30
@ JM4: nếu bạn sử dụng $ host $ trong phần viết lại thay vì server_name và thêm default_server vào lệnh nghe, nó sẽ hoạt động cho mọi miền trên máy chủ của bạn.
Klaas van Schelven

5
Điều quan trọng cần đề cập là 301 được lưu trữ trong bộ nhớ cache cục bộ của bạn mà không hết hạn sử dụng. Không hữu ích khi thay đổi cấu hình
Trefex

9
@everyone Sử dụng chuyển hướng 307 để giữ nội dung POST.
Mahmoud Al-Qudsi

10
Lưu ý rằng bạn phải sử dụng $hostthay vì $server_namenếu bạn đang sử dụng tên miền phụ.
Cá da trơn

276

LƯU Ý: Cách tốt nhất để làm điều này được cung cấp bởi https://serverfault.com/a/401632/3641 - nhưng được lặp lại ở đây:

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

Trong trường hợp đơn giản nhất, máy chủ của bạn sẽ được sửa thành dịch vụ mà bạn muốn gửi cho họ - điều này sẽ thực hiện chuyển hướng 301 đến trình duyệt và URL trình duyệt sẽ cập nhật tương ứng.

Dưới đây là câu trả lời trước, không hiệu quả do regex, 301 đơn giản là tuyệt vời như được hiển thị bởi @kmindi

Tôi đã sử dụng nginx 0.8.39 trở lên và đã sử dụng như sau:

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

Gửi một chuyển hướng vĩnh viễn cho khách hàng.


15
Tôi nghĩ rằng nó phải là 80 - vì đây là nghe http và sau đó nói với khách hàng quay lại dưới dạng https (443).
Michael Neale

3
Đây phải là câu trả lời hàng đầu!
Nathan

3
Đây là câu trả lời đánh thuế nhiều nhất.
Trường hợp

1
đây là cách dễ nhất, nhưng kém an toàn nhất - theo cách này bạn cho phép máy chủ của mình chuyển hướng người dùng đến bất kỳ trang nào mà không cần kiểm tra xem nó có được phép sử dụng trên máy chủ của bạn không. Nếu máy chủ của bạn phục vụ mydomain.co, người dùng độc hại vẫn có thể sử dụng máy chủ của bạn để chuyển hướng người dùng đến các tên miền khác như mydomain.co, chẳng hạn như google.com.
Friedkiwi

10
@ cab0lt không có vấn đề bảo mật ở đây. Phục vụ chuyển hướng không có rủi ro bảo mật. Nếu có các yêu cầu kiểm soát truy cập, những yêu cầu này cần được kiểm tra tại thời điểm trình duyệt yêu cầu URL mới. Trình duyệt sẽ không có quyền truy cập đơn giản trên cơ sở chuyển hướng, và họ cũng không cần chuyển hướng để yêu cầu URL mới.
mc0e

125

Tôi nghĩ cách tốt nhất và duy nhất nên sử dụng chuyển hướng vĩnh viễn HTTP 301 đã di chuyển như thế này:

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

Các HTTP 301 Moved Permanently chuyển hướng cũng là hiệu quả nhất vì không có regex để được đánh giá, theo đã được đề cập pitfails .


Mới HTTP 308 Moved Permanently bảo phương pháp Yêu cầu và được hỗ trợ bởi các trình duyệt chính . Ví dụ: sử dụng 308ngăn trình duyệt thay đổi phương thức yêu cầu từ POSTsang GETyêu cầu chuyển hướng.


Nếu bạn muốn duy trì tên máy và tên miền phụ này là đường đi.

Điều này vẫn hoạt động nếu bạn không có DNS , vì tôi cũng đang sử dụng nó cục bộ. Tôi đang yêu cầu ví dụ với http://192.168.0.100/index.phpvà sẽ được chuyển hướng chính xác https://192.168.0.100/index.php.

Tôi sử dụng listen [::]:80trên máy chủ của mình vì tôi đã bindv6onlythiết lập false, vì vậy nó cũng liên kết với ổ cắm ipv4. thay đổi nó thành listen 80nếu bạn không muốn IPv6 hoặc muốn liên kết ở nơi khác.

Giải pháp từ Saif Bechan sử dụng cái server_namemà trong trường hợp của tôi là localhost nhưng không thể truy cập qua mạng.

Giải pháp từ Michael Neale là tốt, nhưng theo pitfails, có một giải pháp tốt hơn với redirect 301;)


Rất vui khi bạn cố gắng trích dẫn nó, nhưng 301 không hoạt động trên HTTPS.
Trường hợp

5
những gì không làm việc? phần máy chủ đã nêu dành cho lưu lượng truy cập http (không có mã hóa) không được chuyển hướng vĩnh viễn đến máy chủ được mã hóa (phần đó nghe trên 443 (https) không được liệt kê)
kmindi

Tôi đã kiểm tra điều này hoạt động rất tốt với https và mọi thứ - @kmindi Tôi đã cập nhật câu trả lời của mình với tham chiếu đến bạn - vì tôi nghĩ đó là cách đúng đắn và điều này tiếp tục xuất hiện! Công việc tốt đẹp.
Michael Neale

Khi sử dụng yêu cầu tên miền (không phải ip), không hoạt động trừ khi tôi thay đổi '[::]: 80' thành '80'.
Joseph Lust

đó có thể là hành vi dự kiến: trac.nginx.org/nginx/ticket/345 . Tôi cập nhật câu trả lời để mô tả tùy chọn nghe.
kmindi

20

Trong khối máy chủ, bạn cũng có thể làm như sau:

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

2
Cấu hình này khiến máy chủ của tôi tạo ra vòng lặp chuyển hướng
Corkscreewe

Có thể do có một chuyển hướng khác tại chỗ hoặc https không được bật trong trang web / ứng dụng của bạn
Oriol

1
Không ai trong số những người khác dường như làm việc ngoại trừ điều này. Sử dụng phiên bản nginx: nginx / 1.10.0 (Ubuntu)
ThatGuy343

nâng cấp cho https: // $ host $ uri
AMB

4
Đây là con đường để đi nếu bạn đứng sau một loadbalancer!
Antwan

17

Ở trên không hoạt động với các tên miền phụ mới được tạo mọi lúc. ví dụ: AAA.example.com BBB.example.com cho khoảng 30 tên miền phụ.

Cuối cùng có một cấu hình làm việc với những điều sau đây:

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

cảm ơn bạn! nginx sẽ trả lại 301 https://*/hoặc hủy yêu cầu sớm trong các câu trả lời khác tại đây. server_name _;với $hostcâu trả lời đã làm điều đó. +1
zamnuts

1
Điều này là tối ưu! Tuy nhiên, tôi khuyên một số người nên thay thế _bằng tên miền thực tế, ví dụ: .domain.comtôi có hai máy chủ và nginx đã vô tình hướng một trong các máy chủ của tôi đến máy chủ mặc định.
zzz

1
Đây là câu trả lời duy nhất làm việc cho tôi, cảm ơn!
tuyết

Cảm ơn rất nhiều bạn .. Tôi đã thử nhiều giải pháp nhưng không được. Giải pháp này là tuyệt vời và nó đã làm việc cho tôi. tên máy chủ _; Điều này có nghĩa là gì .. Tôi không hiểu. Hãy giải thích cho tôi điều này.
Pavan Kumar

6

Tôi đã đăng một bình luận về câu trả lời đúng từ lâu, rất lâu rồi với một sự điều chỉnh rất quan trọng, nhưng tôi cảm thấy cần phải làm nổi bật sự điều chỉnh này trong câu trả lời của chính nó. Không có câu trả lời nào trước đây an toàn để sử dụng nếu tại bất kỳ thời điểm nào bạn đã cài đặt HTTP không an toàn và mong đợi nội dung người dùng, có biểu mẫu, lưu trữ API hoặc đã định cấu hình bất kỳ trang web, công cụ, ứng dụng hoặc tiện ích nào để nói chuyện với trang web của bạn.

Sự cố xảy ra khi một POSTyêu cầu được gửi đến máy chủ của bạn. Nếu máy chủ phản hồi với 30xchuyển hướng đơn giản , nội dung POST sẽ bị mất. Điều gì xảy ra trình duyệt / khách hàng sẽ nâng cấp theo yêu cầu SSL nhưng hạ cấp các POSTđến một GETyêu cầu. Các POSTtham số sẽ bị mất và yêu cầu không chính xác sẽ được thực hiện cho máy chủ của bạn.

Giải pháp rất đơn giản. Bạn cần sử dụng một HTTP 1.1 307chuyển hướng. Đây là chi tiết trong 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).

Giải pháp, được điều chỉnh từ giải pháp được chấp nhận, là sử dụng 307mã chuyển hướng của bạn:

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

Tôi đang chạy ngnix đằng sau một ELB AWS. ELB đang nói chuyện với ngnix qua http. Vì ELB không có cách nào để gửi chuyển hướng đến khách hàng, tôi kiểm tra tiêu đề và chuyển hướng X-Forwarded-Proto:

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

1

Nếu bạn return 301 https://$host$request_uri;là phản hồi mặc định trên cổng 80, thì máy chủ của bạn sớm muộn cũng có thể nhận được danh sách các proxy mở [1] và bắt đầu bị lạm dụng để gửi lưu lượng truy cập ở nơi khác trên Internet. Nếu nhật ký của bạn chứa đầy những thông điệp như thế này, thì bạn biết điều đó đã xảy ra với bạn:

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

Vấn đề là nó $hostsẽ phản hồi lại bất cứ thứ gì trình duyệt gửi trong Hosttiêu đề hoặc thậm chí tên máy chủ từ dòng mở của HTTP, như thế này:

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

Vì vấn đề đó, một số câu trả lời khác ở đây khuyên bạn nên sử dụng $server_namethay vì $host. $server_nameluôn luôn đánh giá những gì bạn đưa vào server_namekhai báo. Nhưng nếu bạn có nhiều tên miền phụ ở đó hoặc sử dụng ký tự đại diện, điều đó sẽ không hoạt động, bởi vì $server_namechỉ sử dụng mục nhập đầu tiên sau khi server_namekhai báo, và quan trọng hơn là sẽ trả lại ký tự đại diện (không mở rộng nó).

Vậy làm thế nào để hỗ trợ nhiều tên miền trong khi duy trì bảo mật? Trên các hệ thống của riêng tôi, tôi đã xử lý vấn đề nan giải này bằng cách trước tiên liệt kê một default_serverkhối không sử dụng $host, sau đó liệt kê một khối ký tự đại diện:

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

(Bạn cũng có thể liệt kê nhiều tên miền trong khối thứ hai.)

Với sự kết hợp đó, các tên miền chưa từng có sẽ được chuyển hướng đến một nơi nào đó được mã hóa cứng (luôn luôn example.com) và các tên miền phù hợp với chính bạn sẽ đến đúng nơi. Máy chủ của bạn sẽ không hữu ích như một proxy mở, vì vậy bạn sẽ không gặp rắc rối.

Nếu bạn cảm thấy khó chịu, tôi cho rằng bạn cũng có thể làm cho default_serverkhối không khớp với bất kỳ tên miền hợp pháp nào của bạn và phục vụ một cái gì đó gây khó chịu. . . .

[1] Về mặt kỹ thuật "proxy" là từ sai, vì máy chủ của bạn không hoạt động và đáp ứng các yêu cầu cho khách hàng, chỉ gửi một chuyển hướng, nhưng tôi không chắc từ nào sẽ đúng. Tôi cũng không chắc mục tiêu là gì, nhưng nó sẽ lấp đầy các bản ghi của bạn với tiếng ồn và tiêu tốn CPU và băng thông của bạn, vì vậy bạn cũng có thể dừng lại với nó.


0

Có vẻ như không ai thực sự hiểu đúng 100%. Để có các yêu cầu cổng 80, hãy chuyển đến 443 tương đương của chúng cho toàn bộ máy chủ web, bạn cần sử dụng lệnh nghe chứ không phải lệnh server_name để chỉ định tên bắt tất cả . Xem thêm https://nginx.org/en/docs/http/request_ Processing.html

người phục vụ {
    nghe 80 mặc định;
    nghe [::]: 80 mặc định;
      trả về 307 https: // $ máy chủ $ request_uri;
}
  • $ host bắt tên miền phụ.
  • 307 và 308 bao gồm cả URI yêu cầu POST và GET.
  • 307 là tạm thời, thay đổi thành 308 vĩnh viễn sau khi thử nghiệm kỹ lưỡng:

Và hãy chắc chắn rằng bạn kiểm tra những gì đã có trong /etc/nginx/conf.d/ vì thường xuyên hơn tôi không gặp vấn đề trong đó default.conf trả về một số vhost hiện có. Thứ tự làm việc với các vấn đề nginx của tôi luôn bắt đầu bằng cách di chuyển tệp mặc định, đưa nó trở lại bình luận theo từng dòng để xem nó sai ở đâu.


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