proxy 뒤에서 docker의 wordpress, https 적용

이번 글은 Nginx proxy 뒤에 다시 Docker 실행된 wordpress에 https 를 적용하면서 겪었던 문제 및 해결 방법을 공유한 글입니다.

이런 구성을 말로만 설명하려니 조금 복잡한데 아래 글에 있는 구성에서 이 서버에 홈페이지를 추가 하기 위해 wordpress docker container를 실행했습니다.

실제 구성은 다음과 같이 됩니다.

wordpress_docker_https

구성을 보시면 하나의 서버에 다양한 API 서버가 실행되고 있고 각각은 다른 서브 도메인을 사용하고 있습니다. 이들 도메인 간의 proxy 설정을 위해 가장 앞 단에 nginx를 위치하고 있습니다.

기본 설정

이런 구성에서 위 구성의 하단에 있는 wordpress를 이용하여 홈페이지를 새롭게 설치하려고 합니다.  wordpress도 설치를 쉽게 하기 위해 공식적으로 제공하는 Wordpress docker 를 사용하였습니다.  Wordpress docker의 경우 Apache 서버, PHP, Wordpress가 하나의 이미지로 구성된 이미지 파일입니다. Apache의 기본 port는 80으로 실행되어 있습니다. 대략 세부 구성은 다음과 같습니다.

  • 앞 단의 Nginx가 80 포트 사용
  • Docker내의 Apache는 8080으로 구성
  • Nginx에서 다음과 같은 설정을 통해 https 구성 및 Redirect 설정
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    server {
    listen      80;
    server_name www.eryuan.co;
    return 301 https://$host$request_uri;
    }
    server {
    listen 443 ssl;
    server_name www.eryuan.co;
    ssl_certificate /workspace/https_key/www/www.eryuan.co.pem;
    ssl_certificate_key /workspace/https_key/www/www.eryuan.co.key;
    location / {
    proxy_set_header        X-Real-IP         $remote_addr;
    proxy_set_header        X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header        Host              $host;
    proxy_set_header        X-Forwarded-Proto $http_x_forwarded_proto;
    proxy_set_header        X-Forwarded-Port  $http_x_forwarded_port;
    proxy_pass              http://127.0.0.1:8080/;
    }
    }
  • [보완 2020-03-31] 글 작성 후 댓글로 위 설정 중 X-Forworded-Proto  설정을 변경하면 된다고 제안하신 분이 있어 다음과 같이 설정하였습니다. 이렇게 수정한 다음에는 wp-config.php를 수정하지 않아도 정상 동작하였습니다. 참고하세요.
    1
    2
    proxy_set_header        X-Forwarded-Proto https;
    proxy_set_header        X-Forwarded-Port  443;
  • Wordpress의 사이트 URL을 https로 변경
    1
    2
    define( 'WP_HOME', 'https://www.eryuan.co/' );
    define( 'WP_SITEURL', 'https://www.eryuan.co/' );

위 구성에서 다음과 같은 앞 단의 nginx가 80 포트를 사용하고 있기 때문에 Apache의 port는 8080을 설정하였습니다.

https 요청에 대한 처리

이런 구성에서 http://www.eryuan.co로 오는 요청의 경우 위 nginx 설정에 있는 listen 80 설정에서 return 301 항목을 보면 https로 바꾸어서 redirect를 보내고 있습니다. 이렇게 하면 브라우저가 이 응답을 받은 후 다시 https://www.eryuan.co로 요청을 보냅니다.

https://www.eryuan.co 요청을 받으면 listen 443 ssl 설정 부분을 통해 인증서를 찾고, proxy_pass  설정에 의해 로컬의 docker에 설치된 8080 포트로 리다이렉트 합니다. 이때 wordpress docker에 있는 Apache에는 https와 관련된 설정은 아무것도 하지 않고 http 로만 설정된 상태입니다.

발생한 문제

이런 구성에서 wordpress  관리를 위해 https://www.eryuan.co/wp-admin 으로 접속하면 문제가 발생합니다.

wordpress_admin_error

자세히 보면 ERR_TOO_MANY_REDIRECTS 에러가 발생하고 있습니다. 브라우저의 개발자 도구에서 네트워크 상황을 보면 다음과 같습니다.

https_error_details

Response를 302(Redirect) https://www.eryuan.co/wp-admin/ 로 받았기 때문에 다시 서버로 요청하게 되는데 이 요청의 결과도 다시 302 Redirect가 오게 되어 무한 반복되는 문제가 발생합니다.

문제 해결

여러 워드프레스 관련 질의 응답 페이지를 확인해 보았지만 대부분은 docker 자체가 직접 https 를 받는 경우에 대한 설명이라 이들 설명을 바탕으로 조금 응용을 해야만 했습니다. wp-config.php 코드 중 https  요청에 대한 인식 부분을 수정하여 문제를 해결했습니다.

원래 코드는 다음과 같습니다.

1
2
3
4
5
// If we're behind a proxy server and using HTTPS, we need to alert Wordpress of that fact
// see also http://codex.wordpress.org/Administration_Over_SSL#Using_a_Reverse_Proxy
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
        $_SERVER['HTTPS'] = 'on';
}

대략 주석과 코드를 보면 HTTP_X_FORWARDED_PROTO가 https 로 설정되어 있으면 HTTPS 설정을 on  시키겠다는 의미인 것 같습니다. 이 값은 워드프레스의 웹서버로 설정된 Apache 로 부터 받아야 하는데 Apache의 설정이 http로 되어 있기 때문에 이 설정이 on이 되지 않다고 생각을 하고 아래와 같이 강제로 $_SERVER['HTTPS'] = 'on'; 이 코드가 실행되도록 하였습니다.

1
2
3
//if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
        $_SERVER['HTTPS'] = 'on';
//}

if  로직 부분을 주석 처리만 하였습니다.

이렇게 수정한 후에는 정상적으로 잘 동작하는 것을 확인할 수 있었습니다.

[보완 2020-03-31] nginx 설정에서 X-Forwarded-Proto 값을 강제적으로 https 로 설정하면 wp-congif.php 소스 코드를 수정하지 않아도 정상동작합니다. 

결론

워드프레스의 코드에 있는 HTTP_X_FORWARDED_PROTO 이 부분을 앞단의 웹 서버에서 어떻게 전달해야 하는지 정확하게 알면 소스 코드 수정없이 설정만으로 작업을 할 수 있을 것 같은데 거기까지 확인은 해보지 않았습니다. 따라서 필자의 해결 방법은 임시적인 해결 방법이라고 할 수 있습니다. 좀 더 웹서버, 워드 프레스의 동작 원리를 찾아봐야 정확하게 설정하는 방법을 알겠지만 시간적인 여유나 귀챠니즘으로 인해 여기까지만 확인하고 중단하였습니다. 이런 경험을 글로 공유하여 비슷한 문제를 겪으시는 분들에게 조금이나마 도움이 되었으면 하는 바램과 이 부분에 대해 잘 아시는 분의 답변을 기다리기로 했습니다.

[보완] 이 글을 쓴 취지에 맞게 글 공유된 후 바로 댓글로 해결책을 알려주셨습니다. 적용 후 정상 동작하는 것을 확인하였습니다. 본문에 내용 반영하였습니다. 다만 아직도 애매한 부분은 nginx에서 443 listen시 "$http_x_forwarded_proto" 변수 값이 https가 아니고 무엇이지 하는 생각은 듭니다. 구글 검색을 해도 잘 나오지 않네요.

[보완 2차]

다음 글은 박종훈 님이 페북 댓글로 달아주신 내용입니다.

=================

'$http_x_forwarded_proto' 변수와 같이 $http_xxx로 시작하는 변수는 ngx_http_core_module 모듈에서 추가해주는 변수입니다.

(참고: https://nginx.org/en/docs/http/ngx_http_core_module.html)

위 URL을 보시면, 다음과 같이 설명을 하고 있습니다.

'The ngx_http_core_module module supports embedded variables with names matching the Apache Server variables. First of all, these are variables representing client request header fields, such as $http_user_agent, $http_cookie, and so on.'

HTTP 요청헤더의 필드들을 자동으로 변수에 세팅해줍니다. 가령, Cookie 헤더는 $http_cookie로, User-Agent 헤더는 $http_user_agent 변수로 세팅을 해줍니다.

마찬가지로, 요청헤더에 X-Forwarded-Proto 라는 헤더가 있다면 $http_x_forwarded_proto 라는 변수로 세팅되었을 것입니다. (그래서 구글링해도 잘 안나왔을 겁니다ㅠㅠ 모듈에서 자동으로 추가해주는 값이라..)

추측 하건데, 이전에 nginx 앞단에 LB나 다른 프록시 역할을 하는 layer가 있어서 해당 설정을 추가하셨던것이 아닌가 생각됩니다~ 그런데 구성이 바뀌면서 해당 설정이 오히려 문제를 일으켰던것 같습니다^^;

=================


Popit은 페이스북 댓글만 사용하고 있습니다. 페이스북 로그인 후 글을 보시면 댓글이 나타납니다.