nginx에서 SSL을 강제 실행하거나 리디렉션하는 방법은 무엇입니까?


222

다음과 같은 하위 도메인에 가입 페이지가 있습니다. https://signup.example.com

HTTPS를 통해서만 액세스 할 수 있어야하지만 사람들이 어떻게 든 HTTP를 통해 넘어져서 404를 얻을 수 있다고 걱정합니다.

nginx의 내 HTML / 서버 블록은 다음과 같습니다.

html {
  server {
    listen 443;
    server_name signup.example.com;

    ssl                        on;
    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;

    ssl_session_timeout 30m;

    location / {
      root /path/to/my/rails/app/public;
      index index.html;
        passenger_enabled on;
    }
  }
}

내가가는 사람들 http://signup.example.com이 리디렉션 되도록 무엇을 추가 할 수 https://signup.example.com있습니까? (FYI 나는 강제 할 수는 SSL있지만 그것을 피하기를 희망 하는 Rails 플러그인이 있다는 것을 알고 있습니다 )


답변:


145

nginx 함정 에 따르면 $request_uri대신 불필요한 캡처를 생략하는 것이 좋습니다. 이 경우 nginx가 쿼리 인수를 두 배로 늘리지 않도록 물음표를 추가하십시오.

server {
    listen      80;
    server_name signup.mysite.com;
    rewrite     ^   https://$server_name$request_uri? permanent;
}

68
또는 귀하가 연결 한 사이트에 따르면 "BETTER" :return 301 http://domain.com$request_uri;
nh2

13
하나의 의견. $ server_name $은 첫 번째 server_name 변수를 선택합니다. 당신이 당신의 구성에서 비 FQN 이름 그래서 만약이 인식
engineerDave

2
@ nh2 return 301...rewrite 메서드가 실제로 작동하는 동안 사용 하면 "리디렉션이 너무 많습니다"오류가 발생하기 때문에 설명서가 잘못되었습니다 .
Mike Bethany

1
이제는 "나쁜"문서화되었습니다. @MikeBethany return 301는 두 포트를 모두 수신하여 올바른 URL에 대해서도 트리거하지 않는 한 작동하지 않습니다 (예 : 구성 트리거 : 문제 트리거 : serverfault.com/a/474345/29689의 첫 번째 답변을 취하고 if를 생략하십시오 ).
Blaisorblade

1
몇 년 동안 무엇이 바뀌 었는지, 그리고 다른 대답이 더 나은지 궁금합니다 : serverfault.com/a/337893/119666
Ryan

256

공식 사용 방법 에서 설명한 가장 좋은 방법 은 return지시문 을 사용하는 것입니다 .

server {
    listen      80;
    server_name signup.mysite.com;
    return 301 https://$server_name$request_uri;
}

5
최단 답변 및 내 경우에는 완벽하게 작동
mateusz.fiolka

1
이것은 일반적으로 301 Moved Permanently(링크가 영구적으로 이동 했음) 및 다시 쓰기를 반환하기 때문에 권장 됩니다
sgb

1
설정 한 경우에도 "너무 많은 리디렉션"오류가 발생하므로 작동하지 않습니다.proxy_set_header X-Forwarded-Proto https;
Mike Bethany

1
@ MikeBethany listen 443;같은 블록에서 정의 하고 있습니까?
Joe B

2
이것이 정답입니다.
sjas

119

하나의 서버 블록에 모두 유지하려면이 방법이 정확하고 가장 효율적인 방법입니다.

server {
    listen   80;
    listen   [::]:80;
    listen   443 default_server ssl;

    server_name www.example.com;

    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;

    if ($scheme = http) {
        return 301 https://$server_name$request_uri;
    }
}

"rewrite"또는 "if ssl_protocol"등을 사용하는 위의 모든 것은 느리고 더 나쁩니다.

http 프로토콜에서 다시 쓰기 만 실행하면 모든 요청에서 $ scheme 변수를 확인하지 않아도됩니다. 그러나 진지하게, 그것들을 분리 할 필요가없는 사소한 일입니다.

server {
    listen   80;
    listen   [::]:80;

    server_name www.example.com;

    return 301 https://$server_name$request_uri;
}
server {
    listen   443 default_server ssl;

    server_name www.example.com;

    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;
}

8
대단한, 어떤 겁쟁이는이 대답이 맞음에도 불구하고 이유를 말하지 않고이 대답에 투표했습니다. 어쩌면 그 "사악한"이교도 중 하나 일 수도 있습니다. If에 대한 Nginx 문서를 귀찮게한다면 IfIsNOTEvil은 CERTAIN만이 location {} 컨텍스트 내에서 그것을 사용한다는 것을 알 것입니다. 내 대답은 절대적으로 일을하는 올바른 방법입니다!
DELETEDACC 2013

2
나는 이것을 투표하지 않았지만 가장 최신 버전에서 기본값이 'default_server'로 변경되었음을 지적하고 싶습니다.
spuder

두 번째 솔루션이 더 효율적인 경우 첫 번째 솔루션이 가장 효율적일 수 없습니다. 그리고 "만약 모든 요청에서 $ scheme 변수를 검사 할 필요가 없다"고 if를 사용해서는 안되는 이유에 대해서도 설명했다. ifs를 사용하지 않는 요점은 성능뿐만 아니라 선언적이며 명령 적이 지 않은 것입니다.
pepkin88

하나를위한 경우 ($ 방식 = HTTP)
페르난도 코쉬

다른 답변에서 언급했듯이 $ host를 사용해야합니다.
Artem Russakovskii

56

새로운 이중 HTTP 및 HTTPS 서버 정의를 사용하는 경우 다음을 사용할 수 있습니다.

server {
    listen   80;
    listen   [::]:80;
    listen   443 default ssl;

    server_name www.example.com;

    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;

    if ($ssl_protocol = "") {
       rewrite ^   https://$server_name$request_uri? permanent;
    }
}

이것은 저에게 효과적이며 리디렉션 루프를 유발하지 않습니다.

편집하다:

교체 :

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

Pratik의 다시 쓰기 줄로.


2
@DavidPashley 솔루션이 저에게 매력처럼 작용했습니다. 감사합니다
Jayesh Gopalan

1
If you are using the new dual HTTP and HTTPS server definition그런 다음 분리해야합니다.
VBart

2
우아하고 완벽하게 작동합니다!
jacktrade

2
이것은 Laravel / Homestead Nginx 구성에서 저에게 효과적이었습니다.
Jared Eitnier

1
또한 재 작성 라인 return 301 https://$server_name$request_uri;은 이것이 선호되는 방법 이어야 합니다.
Jared Eitnier

27

Host : request 헤더를 유지하고 nginx 함정 에 대한 "GOOD"예제를 따르는 또 다른 변형 :

server {
    listen   10.0.0.134:80 default_server;

    server_name  site1;
    server_name  site2;
    server_name  10.0.0.134;

    return 301 https://$host$request_uri;
}

결과는 다음과 같습니다. $server_name대신에 를 사용 $host하면 항상로 리디렉션됩니다 https://site1.

# curl -Is http://site1/ | grep Location
Location: https://site1/

# curl -Is http://site2/ | grep Location
Location: https://site2/


# curl -Is http://site1/foo/bar | grep Location
Location: https://site1/foo/bar

# curl -Is http://site1/foo/bar?baz=qux | grep Location
Location: https://site1/foo/bar?baz=qux

Note that using $server_name instead of $host would always redirect to https://site1그게 $request_uri아닌가?
Jürgen Paul

2
$request_uri호스트 또는 도메인 이름이 없습니다. 즉, 항상 "/"문자로 시작합니다.
피터

2
최고의 답변.
Ashesh

3
이 답변이 왜 투표에서 그렇게 낮은 지 잘 모르겠습니다. 사용할 가치가있는 유일한 제품입니다.
zopieux

2
칸트는 이렇게 많은 사람들이 그것을 할 수있는 올바른 방법이다 SERVER_NAME $ 사용할 생각
그렉 에니스

3

쿠키에 '보안'을 설정했는지 확인하십시오. 그렇지 않으면 쿠키가 HTTP 요청으로 전송되고 Firesheep와 같은 도구로 잡을 수 있습니다.


1
server {
    listen x.x.x.x:80;

    server_name domain.tld;
    server_name www.domian.tld;
    server_name ipv4.domain.tld;

    rewrite     ^   https://$server_name$request_uri? permanent;
}

생각보다 잘 작동합니다. xxxx는 서버의 IP를 나타냅니다. Plesk 12를 사용하는 경우 "/var/www/vhosts/system/domain.tld/conf"디렉토리의 "nginx.conf"파일을 원하는 도메인으로 변경하면됩니다. 구성을 저장 한 후 nginx 서비스를 다시 시작하는 것을 잊지 마십시오.


rewrite ^ https://$host$request_uri? permanent; vhost에 여러 서버 이름이있을 수 있으므로 더 나은 솔루션이 될 것입니다

0

이것이 가장 간단한 해결책이라고 생각합니다. 비 HTTPS 및 비 WWW 트래픽을 모두 HTTPS 및 www로만 강제합니다.

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

편집-2018 년 4 월 : IF가없는 솔루션은 여기 내 게시물에서 찾을 수 있습니다 : https : //.com/a/36777526/6076984


1
IF 조건이 nginx 세계에서 악하고 비효율적 인 것으로 간주되지 않습니까?
PKHunter

예, 일반적으로 있습니다. 그러나이 간단한 점검을 위해 나는 추측하지 않을 것입니다. 더 많은 코드 작성이 필요한 적절한 구성 파일이 있지만 IF를 완전히 피할 수는 없습니다.
stamster

Google은 303 대신 301을 사용하는 것이 좋습니다. 출처 : support.google.com/webmasters/answer/6073543?hl=ko
dylanh724

@DylanHunt-테스트 용으로 303을 떠났습니다. 첫 번째 핸들러는 301로 설정되었으며 두 번째는 변경하는 것을 잊었습니다 :) 또한 IF가없는 솔루션 : stackoverflow.com/a/36777526/6076984
stamster
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.