HTTPS + NGINX + PHP-FPM + SQLITE
SSL 인증서로 안전하게 MyBoard 연결하기
인증서 파일 fullchain.cer와 개인키 key를 Nginx에 연결하고, Nginx가 HTTPS를 처리한 뒤 로컬 PHP-FPM과 SQLite로만 요청을 전달하도록 구성하는 순서입니다.
전체 구조 먼저 이해하기
사용자는 https://example.com으로 접속합니다. Nginx가 443번 포트에서 인증서를 사용해 암호화를 종료하고, PHP 요청만 로컬 PHP-FPM으로 전달합니다. PHP-FPM은 MyBoard의 SQLite 파일을 읽고 쓰며, SQLite는 인터넷에 직접 열리는 서버가 아닙니다.
브라우저
│ HTTPS :443
▼
Nginx ── TLS 인증서 확인
│ FastCGI 127.0.0.1:9000 또는 PHP-FPM 소켓
▼
PHP-FPM ── MyBoard
│
▼
data/board.sqlite
Termux 일반 사용자로 실행하면 443번 포트를 직접 열 수 없을 수 있습니다. 이 경우 Nginx는 8443에서 실행하고 공유기나 외부 프록시에서 443번 요청을 전달하세요. 루팅으로 root 권한을 부여하는 것보다 높은 포트와 포트포워딩을 우선 권장합니다.
1. 인증서 파일 준비
인증서 발급 기관에서 받은 파일 이름을 이 문서의 예시와 맞춥니다. fullchain.cer는 서버 인증서와 중간 인증서를 포함한 공개 인증서이고, key는 외부에 공개하면 안 되는 개인키입니다. 인증서의 도메인과 실제 접속할 도메인이 일치해야 합니다.
mkdir -p $HOME/.ssl/example.com
cp fullchain.cer $HOME/.ssl/example.com/fullchain.cer
cp key $HOME/.ssl/example.com/key
chmod 700 $HOME/.ssl
chmod 700 $HOME/.ssl/example.com
chmod 644 $HOME/.ssl/example.com/fullchain.cer
chmod 600 $HOME/.ssl/example.com/key
ls -l $HOME/.ssl/example.com
인증서와 개인키는 myboard 문서 루트나 data/ 안에 저장하지 마세요. Nginx가 읽을 수 있어야 하지만 브라우저가 URL로 내려받을 수 있는 위치여서는 안 됩니다. 개인키를 메신저, Git 저장소, 게시물 첨부파일로 공유하지도 마세요.
2. PHP-FPM과 SQLite 확인
PHP CLI와 PHP-FPM의 버전 및 SQLite 확장을 확인합니다. PHP-FPM의 listen 값이 아래 Nginx 설정의 fastcgi_pass와 같아야 합니다.
pkg update && pkg upgrade
pkg install nginx php php-fpm
php -v
php-fpm -v
php -m | grep -i sqlite
php-fpm -tt 2>&1 | grep -E 'listen|configuration file'
pdo_sqlite와 sqlite3가 표시되어야 하며, PHP-FPM 설정에서 listen = 127.0.0.1:9000을 사용한다면 Nginx도 같은 주소를 사용합니다. Unix 소켓이라면 실제 소켓 경로로 바꾸고 Nginx 프로세스가 그 소켓에 접근할 수 있는지 확인하세요.
cd $HOME/www/myboard
mkdir -p data uploads
php install.php
chmod 750 data
chmod 640 data/board.sqlite
SQLite 파일은 PHP-FPM만 읽고 쓸 수 있어야 합니다. 실제 실행 사용자와 그룹에 맞게 권한을 조정하되, 서버 전체에 쓰기 권한을 주는 chmod 777은 사용하지 마세요.
3. 구형 Android·Termux 호환성 확인
구형 스마트폰에서는 인증서 파일보다 운영체제와 Termux 패키지 저장소가 먼저 문제될 수 있습니다. 특히 Android 5·6용 Termux는 앱이 다시 제공된 적은 있지만 패키지 업데이트 계획이 없고, 예전 저장소가 종료된 상태이므로 Nginx·PHP-FPM·OpenSSL을 새로 설치하거나 보안 갱신하기 어렵습니다. 이 환경은 테스트용으로만 보고 인터넷 공개 서버로는 권장하지 않습니다.
먼저 실제 기기에서 다음 정보를 확인하세요.
getprop ro.build.version.release
getprop ro.build.version.sdk
termux-info
uname -m
pkg update
pkg install openssl nginx php php-fpm
openssl version
nginx -V 2>&1
php -v
php-fpm -v
Android 버전, Termux 배포판, CPU 아키텍처, 저장소 갱신 결과와 각 프로그램 버전을 기록합니다. pkg update에서 저장소 오류가 나거나 패키지 설치가 불가능하면 SSL 연동 이전에 Termux 환경부터 교체해야 합니다. 출처가 불분명한 오래된 APK나 종료된 저장소를 섞어 사용하지 마세요.
인증서와 개인키가 Nginx가 읽을 수 있는 PEM 형식인지도 확인합니다.
# 첫 줄이 -----BEGIN CERTIFICATE-----인지 확인
head -n 1 $HOME/.ssl/example.com/fullchain.cer
# 인증서 체인과 유효기간 확인
openssl x509 -in $HOME/.ssl/example.com/fullchain.cer -noout -subject -issuer -dates
# 개인키 형식과 무결성 확인
openssl pkey -in $HOME/.ssl/example.com/key -check -noout
# 인증서와 개인키가 같은 쌍인지 확인
openssl x509 -in $HOME/.ssl/example.com/fullchain.cer -pubkey -noout | openssl pkey -pubin -outform DER | sha256sum
openssl pkey -in $HOME/.ssl/example.com/key -pubout | openssl pkey -pubin -outform DER | sha256sum
두 해시가 같아야 인증서와 개인키가 한 쌍입니다. openssl x509 또는 openssl pkey에서 오류가 나면 파일 확장자만 바꾸지 말고 발급기관에서 PEM 인증서와 PEM 개인키를 다시 받아야 합니다.
실무 판단 기준은 다음과 같습니다. Android 5·6 또는 오래된 Termux 저장소라면 운영 공개를 피하고, 최소한 패키지 갱신과 nginx -t가 정상인 별도 최신 Android/Termux 환경으로 이전하세요. 구형 기기에서 로컬 테스트만 하는 경우에도 인증서 만료와 취약한 TLS 라이브러리 문제를 감수해야 합니다.
4. Nginx HTTPS 설정 작성
현재 Nginx가 읽는 설정 파일에 아래 내용을 추가합니다. Termux는 보통 $PREFIX/etc/nginx/nginx.conf, Debian·Ubuntu는 /etc/nginx/sites-available/ 또는 /etc/nginx/conf.d/를 사용합니다. 인증서 경로와 PHP-FPM 연결 주소는 자신의 환경에 맞게 바꾸세요.
server {
listen 443 ssl;
server_name example.com www.example.com;
root /data/data/com.termux/files/home/www/myboard;
index index.php index.html;
ssl_certificate /data/data/com.termux/files/home/.ssl/example.com/fullchain.cer;
ssl_certificate_key /data/data/com.termux/files/home/.ssl/example.com/key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~* \.(sqlite|sqlite3|db|sql|bak|key)$ {
deny all;
}
location ~ ^/(?:config|db|schema|install)\.php$ {
deny all;
}
location ~ \.php$ {
try_files $uri =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTPS on;
fastcgi_param HTTP_X_FORWARDED_PROTO https;
fastcgi_pass 127.0.0.1:9000;
}
}
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
인증서가 갱신될 때는 파일을 교체한 뒤 Nginx 설정을 다시 읽어야 합니다. ssl_certificate_key에는 인증서가 아니라 개인키 파일인 key를 지정하는 점을 확인하세요. HTTP 80번 포트를 사용할 수 없는 Termux 환경에서는 두 번째 서버 블록을 생략하고 443 대신 8443을 사용합니다.
5. 설정 검사와 서비스 시작
서비스를 재시작하기 전에 항상 설정 문법과 인증서 파일 접근 권한을 검사합니다. 오류가 나면 재시작하지 말고 표시된 파일과 줄을 먼저 수정하세요.
nginx -t
php-fpm -t
php-fpm
nginx
curl -I https://example.com
curl -I http://example.com
HTTPS 응답에서 200 또는 애플리케이션의 정상 리다이렉트가 나오고, HTTP 응답이 HTTPS 주소로 301 이동하면 기본 연결이 완료된 것입니다. 브라우저의 자물쇠를 눌러 인증서 도메인과 만료일도 확인하세요.
Termux에서 Nginx가 8080 또는 8443으로 동작한다면 다음처럼 확인합니다.
curl -I http://127.0.0.1:8080
curl -k -I https://127.0.0.1:8443
ss -ltnp | grep -E ':(8080|8443|9000)\b'
6. 운영 중 보안 점검
- 개인키는
chmod 600으로 제한하고 백업 파일도 같은 수준으로 보호합니다. - PHP-FPM은
127.0.0.1또는 Unix 소켓에서만 듣게 하여 인터넷에 직접 노출하지 않습니다. data/board.sqlite, 설정 파일, 인증서 개인키, 백업 파일은 웹에서 내려받을 수 없게 합니다.- 관리자 비밀번호를 기본값에서 변경하고
install.php는 초기화 뒤 차단하거나 삭제합니다. - 인증서 만료일과 갱신 작업을 정기적으로 확인하고 갱신 뒤
nginx -t를 실행합니다. - SQLite는 DB 서버가 아니므로 외부에서 1433·3306·5432 같은 DB 포트를 열 필요가 없습니다.
- 운영 전에
data/와 업로드 파일을 함께 백업하고 복구 테스트를 한 번 진행합니다.
# 인증서 만료일 확인
openssl x509 -in $HOME/.ssl/example.com/fullchain.cer -noout -subject -issuer -dates
# SQLite 무결성과 파일 권한 확인
sqlite3 $HOME/www/myboard/data/board.sqlite "PRAGMA integrity_check;"
ls -ld $HOME/.ssl/example.com
ls -l $HOME/.ssl/example.com/key $HOME/www/myboard/data/board.sqlite
인증서 파일과 개인키의 소유자·권한을 바꾼 뒤에는 Nginx가 실제로 읽을 수 있는지 nginx -t로 다시 확인하세요. 권한을 무작정 넓히는 대신 Nginx 실행 사용자와 파일 그룹을 먼저 확인하는 것이 안전합니다.
문제가 생겼을 때 확인 순서
- 인증서 오류:
nginx -t, 인증서 경로, 개인키 권한, 인증서 도메인을 확인합니다. - 502 Bad Gateway: PHP-FPM 실행 여부와
listen/fastcgi_pass값이 같은지 확인합니다. - SQLite 오류:
pdo_sqlite,sqlite3,data폴더와 DB 파일의 쓰기 권한을 확인합니다. - 파일이 노출됨: Nginx의 민감 파일 차단 규칙과 문서 루트 밖의 인증서 저장 위치를 확인합니다.
- 외부 접속 불가: DNS, 공유기 443 포트포워딩, 스마트폰 내부 IP, Termux의 실제 수신 포트를 순서대로 확인합니다.