[친구하자] HTTPS/HTTP Mixed Content 에러 해결하기

배포 후 HTTPS 프론트엔드에서 HTTP 백엔드로 요청이 차단되는 Mixed Content 에러를 Nginx와 Certbot으로 해결한 과정을 정리했습니다.

배포 후 첫 번째 난관: Mixed Content 에러

개발 환경에서는 문제없이 동작하던 API 통신이 배포 후 갑자기 차단되기 시작했다. 브라우저 콘솔에 찍힌 에러 메시지는 ‘Mixed Content’… 대체 뭐가 문제일까?

1. 문제 발견

상황

로컬 환경에서는 완벽하게 동작하던 프론트엔드와 백엔드 통신이 배포 후 전혀 동작하지 않았다.

개발 환경 (정상 동작 ✅):
- Frontend: http://localhost:3000
- Backend:  http://localhost:8080
- 통신: HTTP ↔ HTTP

배포 환경 (통신 차단 ❌):
- Frontend: https://front-domain-url (Vercel)
- Backend:  http://43.xxx.xxx.xxx:8080 (EC2)
- 통신: HTTPS ↔ HTTP

발견한 에러

브라우저 콘솔에 다음과 같은 에러가 발생했다:

  • 에러 이미지

API 요청 자체가 브라우저에서 차단되어 백엔드까지 도달조차 하지 못하는 상황이었다.

2. Mixed Content란?

정의

Mixed Content는 HTTPS 페이지에서 HTTP 리소스(이미지, 스크립트, API 등)를 요청할 때 발생하는 보안 문제다.

왜 차단될까?

HTTP vs HTTPS의 차이:

HTTP (Port 80):
- 평문 통신
- 데이터 암호화 없음
- 중간자 공격(MITM) 취약

HTTPS (Port 443):
- SSL/TLS 암호화 통신
- 데이터 무결성 보장
- 인증서 기반 신뢰

HTTPS로 암호화된 페이지에서 HTTP 리소스를 불러오면:

  1. 암호화된 통신의 의미가 사라짐 - 일부 데이터가 평문으로 전송
  2. 중간자 공격 위험 - HTTP 부분에서 데이터 가로채기/조작 가능
  3. 사용자 신뢰 훼손 - 브라우저 보안 경고 표시

따라서 모던 브라우저들은 기본적으로 Mixed Content를 차단한다.

왜 개발 환경에서는 문제없었나?

개발 환경: http://localhost:3000 → http://localhost:8080
→ 둘 다 HTTP, 프로토콜 일치 ✅

배포 환경: https://vercel.app → http://ec2-server
→ HTTPS와 HTTP, 프로토콜 불일치 ❌

Vercel은 자동으로 HTTPS를 제공하지만, EC2에 직접 배포한 백엔드는 기본적으로 HTTP로 동작한다. 이 프로토콜 불일치가 문제의 원인이었다.

3. 해결 방법 선택

Mixed Content 문제를 해결하려면 백엔드도 HTTPS로 제공해야 한다. 찾아본 방법은 크게 2가지였다:

방법 1: AWS Application Load Balancer (ALB)

장점:
✅ AWS 콘솔에서 간단하게 설정
✅ SSL 인증서 관리 자동화 (ACM)
✅ 로드 밸런싱, 헬스 체크 등 추가 기능

단점:
❌ 시간당 과금 (약 $0.025/시간 ≈ $18/월)
❌ 데이터 전송량 추가 과금
❌ 토이 프로젝트에는 과한 비용

방법 2: Nginx + Certbot (선택!)

장점:
✅ 완전 무료
✅ Let's Encrypt 무료 SSL 인증서
✅ 가볍고 성능 우수
✅ 리버스 프록시로 추가 활용 가능

단점:
❌ 직접 설정 필요
❌ 인증서 갱신 관리 필요 (자동화 가능)

비용을 고려해 Nginx + Certbot을 선택했다. 많은 개발자들의 과금 사례를 보고 ALB는 부담스러웠고, Nginx로 충분히 해결 가능한 문제였기 때문이다.

4. Nginx + Certbot 설정

4.1. 사전 준비

다음이 갖춰져 있어야 한다.

  • EC2 인스턴스: 백엔드가 배포된 서버 (Ubuntu 또는 Amazon Linux 권장)
  • 도메인: 백엔드용 도메인 또는 서브도메인 (예: api.mydomain.com)
  • 도메인 DNS 설정: EC2 퍼블릭 IP를 가리키는 A 레코드 등록
  • 방화벽: 인스턴스 보안 그룹에서 80, 443 포트 허용

도메인이 EC2를 바라보고, 80/443 포트가 열려 있어야 Certbot 인증서 발급이 가능하다.

4.2. Nginx 설치

# Ubuntu
sudo apt update
sudo apt install nginx -y

# 서비스 시작 및 부팅 시 자동 시작
sudo systemctl start nginx
sudo systemctl enable nginx

4.3. Nginx 리버스 프록시 설정 (HTTP)

먼저 HTTP로 Nginx가 백엔드로 프록시되도록 설정한다. Certbot은 80 포트를 사용해 인증을 진행한다.

# /etc/nginx/sites-available/backend (예시)
server {
    listen 80;
    server_name api.your-domain.com;  # 본인 도메인으로 변경

    location / {
        proxy_pass http://127.0.0.1:8080;  # Spring Boot 기본 포트
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

설정 활성화 후 문법 검사:

sudo ln -s /etc/nginx/sites-available/backend /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

이제 http://api.your-domain.com으로 접속하면 백엔드(8080)로 프록시되는지 확인할 수 있다.

4.4. Certbot으로 SSL 인증서 발급

# Ubuntu
sudo apt install certbot python3-certbot-nginx -y

# Nginx용 자동 설정으로 인증서 발급
sudo certbot --nginx -d api.your-domain.com

이메일 입력, 약관 동의, 리다이렉트 선택(HTTPS로 리다이렉트 권장)까지 진행하면 Certbot이 Nginx 설정에 SSL 블록을 자동으로 추가해 준다.

4.5. 인증서 갱신 (자동화)

Let’s Encrypt 인증서는 90일마다 갱신해야 한다. cron으로 자동 갱신하는 방법이다.

# 갱신 테스트
sudo certbot renew --dry-run

# cron 등록 (매일 새벽 3시 확인 후 필요 시 갱신)
sudo crontab -e
# 다음 한 줄 추가
0 3 * * * certbot renew --quiet --post-hook "systemctl reload nginx"

이렇게 하면 Nginx + Certbot 설정과 갱신까지 한 번에 갖춰진다.

5. 해결 결과

적용 후 구조

배포 환경 (해결 후 ✅):
- Frontend: https://front-domain (Vercel)
- Backend:  https://api.your-domain.com (Nginx + Certbot on EC2)
- 통신: HTTPS ↔ HTTPS

프론트엔드는 그대로 Vercel의 HTTPS URL을 쓰고, 백엔드만 https://api.your-domain.com으로 노출되도록 바꿨다.

확인한 점

  1. Mixed Content 에러 제거: 브라우저 콘솔에 더 이상 Mixed Content 경고/차단이 뜨지 않았다.
  2. API 통신 정상: 로그인, API 호출 등이 배포 환경에서도 정상 동작했다.
  3. 비용: ALB 없이 Nginx + Let’s Encrypt만 사용해 추가 비용 없이 해결했다.

정리

  • 원인: HTTPS 프론트엔드에서 HTTP 백엔드로 요청을 보내면서 브라우저가 Mixed Content로 차단함.
  • 해결: EC2에 Nginx를 두고, Certbot으로 HTTPS를 붙여 백엔드를 HTTPS로 제공.
  • 부가 효과: 리버스 프록시로 인해 실제 앱은 8080 등 내부 포트만 쓰고, 80/443과 SSL 종료는 Nginx가 담당하게 되어 구조가 더 단순해졌다.

토이 프로젝트 수준에서는 Nginx + Certbot 조합만으로도 HTTPS 전환과 Mixed Content 해결을 무리 없이 할 수 있었다.