호스팅에서 Sectigo 인증서를 발급받아 직접 설치했습니다. 만료는 2027년 2월 24일이고 수동 갱신입니다. 달력에 적어뒀습니다. 이런 건 반드시 잊어버립니다.
삽질 ① "Unable to load certificate"
인증서를 받으면 보통 파일이 여러 개 옵니다. 서버 인증서 하나와 중간 인증서 한두 개입니다. 이걸 순서대로 이어 붙여 fullchain.pem을 만들어야 합니다.
윈도우에서 받은 파일들을 합쳐서 올렸더니 nginx가 거부했습니다.
nginx: [emerg] SSL_CTX_use_certificate_chain_file(...) failed
(SSL: error:0480006C:PEM routines::no start line)
파일을 열어보면 멀쩡해 보입니다. 내용도 다 있습니다. 원인은 줄바꿈이었습니다.
인증서 파일 중 하나가 마지막 줄에 개행 문자 없이 끝나 있었습니다. 그래서 이어 붙였더니 이렇게 됐습니다.
-----END CERTIFICATE----------BEGIN CERTIFICATE-----
앞 인증서의 끝과 뒤 인증서의 시작이 한 줄에 붙어버린 겁니다. 사람 눈으로 대충 보면 잘 안 보이고, 텍스트 에디터에서 줄바꿈을 보이게 켜두지 않으면 더 안 보입니다.
여기에 윈도우의 CRLF까지 섞여 있었습니다. 결국 이렇게 처리했습니다.
tr -d '\r' < in.crt | awk '{print} /-----END CERTIFICATE-----/{print ""}' > out.pem
tr로 CR을 지우고, awk로 END CERTIFICATE 줄 뒤에 빈 줄을 강제로 넣습니다. 이러면 원본이 어떻게 생겼든 항상 분리됩니다.
안전장치를 넣었다
같은 실수를 반복하지 않으려고 설치 스크립트에 검사를 두 개 넣었습니다.
- 인증서 개수 확인 — 합쳐진 파일의
BEGIN CERTIFICATE개수가 2개 미만이면 중단합니다. 체인이 제대로 안 붙었다는 뜻이니까요. - 키와 인증서 짝 확인 — 개인키와 인증서의 modulus를 각각 md5로 뽑아서 일치하는지 봅니다. 엉뚱한 키를 올리면 nginx는 뜨는데 브라우저에서만 실패하는, 제일 찾기 싫은 상태가 됩니다.
둘 다 몇 줄짜리 검사인데, 이게 없었으면 다음에 또 같은 자리에서 헤맸을 겁니다.
삽질 ② nginx가 http2를 모른다
설정을 고치고 재시작했더니 이렇게 나왔습니다.
unknown directive "http2"
검색해서 찾은 최신 문법을 그대로 썼기 때문입니다.
listen 443 ssl;
http2 on; # nginx 1.25 이상
설치된 건 1.24였습니다. 1.24까지는 이렇게 씁니다.
listen 443 ssl http2; # 1.24 문법
다행히 스크립트에 넣어둔 자동 롤백이 동작해서 이전 설정으로 되돌아갔습니다. 설정을 바꾸기 전에 백업을 뜨고, nginx -t가 실패하면 백업으로 되돌린 뒤 재시작하는 아주 단순한 구조입니다.
이런 롤백을 넣어두는 게 원격 서버에서는 특히 중요합니다. 설정이 깨진 채로 nginx가 안 뜨면 사이트가 통째로 죽은 상태가 되니까요.
삽질 ③ robots.txt가 404
SSL과 직접 상관은 없는데 같은 날 걸린 문제입니다. /robots.txt가 404를 내고 있었습니다.
범인은 제가 넣은 이 블록이었습니다.
location = /robots.txt { access_log off; log_not_found off; }
로그를 줄이려고 넣은 건데, location 블록이 매칭되면서 PHP로 넘어가는 경로를 끊어버렸습니다. 당시에는 robots.txt를 동적으로 만들고 있었기 때문에 파일이 실제로 존재하지 않았습니다.
location = /robots.txt {
access_log off;
log_not_found off;
try_files $uri /index.php?$args;
}
검색엔진이 제일 먼저 읽는 파일이 404를 내고 있었으니, 안 고쳤으면 색인 자체가 이상하게 꼬였을 겁니다.
배포 계정을 따로 만들었다
FileZilla로 파일을 올리려고 SFTP 전용 계정을 하나 만들었습니다. root로 접속해서 올리는 건 위험하기도 하고, 실수로 엉뚱한 디렉터리를 건드릴 수 있습니다.
- 셸을
nologin으로 지정 — SSH로 명령을 실행할 수 없습니다 ForceCommand internal-sftp— 파일 전송만 허용합니다www-data그룹에 넣고 디렉터리에 setgid2775— 올린 파일의 그룹이 자동으로 웹서버 그룹이 됩니다
마지막 setgid가 특히 편합니다. 이걸 안 해두면 파일을 올릴 때마다 권한 때문에 403이 납니다.
정리
세 가지 다 보이지 않는 문자와 버전 차이가 원인이었습니다. 코드 로직 문제는 하나도 없었습니다.
그리고 이때 배운 것 하나. 검색해서 나온 설정을 그대로 붙여넣기 전에 내가 쓰는 버전을 먼저 확인한다. nginx -v 한 번이면 끝날 일이었습니다.