Published on

OpenAI Responses API 401 403 인증오류 점검 가이드

Authors

서론

운영 환경에서 OpenAI Responses API를 붙여놓고 가장 사람을 미치게 하는 에러가 401 Unauthorized403 Forbidden입니다. 코드가 어제까지 잘 돌았는데 갑자기 401이 뜨거나, 로컬에서는 되는데 배포만 하면 403이 터지는 식이죠. 더 골치 아픈 점은 둘 다 “인증/권한”처럼 보이지만 원인이 완전히 다르다는 것입니다.

이 글은 “키가 틀렸나?” 수준에서 끝내지 않고, 키·프로젝트·RBAC(역할 기반 접근 제어)·헤더/엔드포인트 불일치·프록시/게이트웨이 변조까지 실제 현업에서 자주 밟는 지뢰를 “위에서 아래로” 빠르게 확인하도록 체크리스트 형태로 정리합니다.


401 vs 403 먼저 구분하기

401 Unauthorized가 의미하는 것

대부분 아래 케이스 중 하나입니다.

  • API 키가 없거나(환경변수 누락)
  • API 키가 잘못되었거나(복사 실수/공백/개행)
  • Authorization 헤더 형식이 틀렸거나
  • 호출 대상 호스트가 OpenAI가 아닌데 OpenAI 키를 보내고 있거나(프록시/사내 게이트웨이)

즉 **“신원 확인 실패”**에 가깝습니다.

403 Forbidden이 의미하는 것

대부분 아래 케이스입니다.

  • 키는 유효하지만 해당 리소스 접근 권한이 없음
  • 조직/프로젝트가 다르거나, 프로젝트가 비활성화됨
  • 모델/기능 사용 권한이 없는 프로젝트에서 호출
  • RBAC에서 해당 키(또는 사용자/서비스 계정)에 필요한 Role이 없음

즉 **“신원은 확인했는데 권한이 없다”**에 가깝습니다.


0단계 재현 최소화 요청/응답 로그 확보

401/403은 “추측”으로 고치면 시간이 오래 걸립니다. 아래 3가지는 반드시 확보하세요.

  • 요청 URL(호스트 포함)
  • 요청 헤더 중 Authorization, OpenAI-Project(또는 프로젝트 관련 헤더), Content-Type
  • 응답 바디의 에러 코드/메시지(가능하면 전체)

Python이라면 httpx에서 이벤트 훅으로 헤더/URL만 안전하게 로깅하는 방식이 좋습니다(키는 마스킹).

import httpx

def mask(s: str, keep=6):
    if not s:
        return s
    return s[:keep] + "…" + s[-4:]

class LogTransport(httpx.BaseTransport):
    def __init__(self, transport):
        self._t = transport

    def handle_request(self, request):
        auth = request.headers.get("Authorization", "")
        print("URL:", request.url)
        print("Authorization:", mask(auth))
        print("OpenAI-Project:", request.headers.get("OpenAI-Project"))
        return self._t.handle_request(request)

client = httpx.Client(transport=LogTransport(httpx.HTTPTransport()))

1단계 401 체크리스트 키/헤더/엔드포인트

1 키가 정말 주입됐는지(환경변수/시크릿)

가장 흔한 사고는 배포 환경에서 시크릿 키가 비어 있는 경우입니다.

  • Kubernetes: Secret는 있는데 envFrom/env 매핑이 빠짐
  • GitHub Actions: secrets 이름 오타
  • Docker: --env-file 경로가 다름

실행 시점에 아래처럼 길이만 확인하세요(전체 출력 금지).

python -c "import os; k=os.getenv('OPENAI_API_KEY',''); print('len=',len(k))"

길이가 0이면 코드 문제가 아니라 배포/시크릿 주입 문제입니다.

2 Authorization 헤더 형식 확인

Responses API는 보통 아래 형태가 기본입니다.

  • Authorization: Bearer <API_KEY>

사내 프록시나 커스텀 클라이언트에서 Token으로 보내거나, Bearer 뒤에 공백이 2개 들어가도 실패할 수 있습니다.

3 개행/공백이 섞인 키 복사 실수

특히 .env에 붙여넣을 때 마지막에 \r이 들어가거나 따옴표가 섞여 실패합니다.

import os
k = os.environ["OPENAI_API_KEY"]
print(repr(k[-5:]))  # '\r' 같은 게 보이면 바로 정리

.env는 가능하면 따옴표 없이:

OPENAI_API_KEY=sk-...실제키...

4 엔드포인트/호스트가 맞는지

운영에서 401이 나는데 로컬은 되는 경우, 호스트가 바뀌는 경우가 많습니다.

  • 사내 API Gateway를 거치며 Authorization 헤더가 제거됨
  • 프록시가 다른 upstream으로 라우팅

반드시 최종 URL이 기대한 호스트인지 확인하세요.

5 SDK 버전/호출 방식 혼용

Responses API와 Chat Completions/Assistants를 섞어 쓰다가, 서로 다른 클라이언트 초기화 방식으로 헤더가 누락되는 경우가 있습니다.

Python 예시(Responses API):

from openai import OpenAI
import os

client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])

resp = client.responses.create(
    model="gpt-4.1-mini",
    input="ping"
)
print(resp.output_text)

요청 스키마 자체 문제로 400이 나는 경우는 인증과 별개이니, 400 디버깅은 별도 체크리스트를 참고하세요: OpenAI Responses API 400 invalid_request_error 원인과 해결


2단계 403 체크리스트 프로젝트/RBAC/모델 권한

403은 “키가 유효한데, 너에게 이 작업을 허용하지 않는다”입니다. 여기서부터는 조직/프로젝트/RBAC가 핵심입니다.

1 프로젝트가 맞는지 OpenAI-Project 헤더/설정 확인

실무에서 가장 흔한 403 패턴:

  • 개발자가 A 프로젝트에서 키를 발급
  • 운영은 B 프로젝트를 바라보도록 설정
  • 혹은 프롬프트/모델 사용 권한이 A에만 있음

프로젝트 스코프를 요구하는 구성에서는 OpenAI-Project(또는 SDK의 프로젝트 설정)가 올바른지 확인하세요.

HTTP 호출 예시(직접 호출 시):

curl https://api.openai.com/v1/responses \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "OpenAI-Project: $OPENAI_PROJECT" \
  -d '{"model":"gpt-4.1-mini","input":"ping"}'
  • 키는 A 프로젝트 소속인데 OpenAI-Project를 B로 보내면 403이 날 수 있습니다.
  • 반대로 프로젝트 헤더가 필요한데 누락되어도 403/401 형태로 실패할 수 있습니다(환경/정책에 따라 다름).

2 RBAC 역할 누락 서비스 계정/키 권한 점검

조직에서 RBAC를 켜면, 같은 키라도 다음이 달라집니다.

  • 어떤 프로젝트에 접근 가능한지
  • 어떤 모델/기능을 호출 가능한지

확인 포인트:

  • 키를 발급한 주체가 개인 계정인지, 서비스 계정인지
  • 해당 계정이 프로젝트에서 최소 Developer 이상의 역할을 갖는지(조직 정책에 따라)
  • 운영에서 사용하는 키가 “읽기 전용” 또는 제한된 역할에 묶여 있지 않은지

증상 예시

  • 로컬(개인 키)은 OK, 운영(서비스 키)은 403
  • 특정 모델만 403, 다른 모델은 OK → 모델 사용 권한/정책 문제 가능성 큼

3 모델 접근 권한/정책 제한

프로젝트 단위로 “허용 모델 리스트”가 걸려 있으면, 호출 자체는 인증되지만 해당 모델에서 403이 납니다.

  • 운영 프로젝트에서 gpt-4.1 계열이 막혀 있음
  • 비용 통제 정책으로 특정 모델이 제한됨

이때는 코드를 고치는 게 아니라 프로젝트 정책(allowed models) 또는 RBAC를 조정해야 합니다.

4 결제/사용 제한으로 인한 거부

조직/프로젝트의 결제 상태나 사용 제한(예: 예산 한도, 정책 기반 차단)에 의해 403이 발생하는 경우도 있습니다. 에러 메시지에 “billing”, “quota”, “policy” 류의 힌트가 있으면 콘솔에서 프로젝트 상태를 먼저 확인하세요.


3단계 배포 환경에서만 터질 때 프록시·게이트웨이·스트리밍 이슈

1 프록시가 Authorization 헤더를 제거/변조

Nginx/Envoy/API Gateway를 통과할 때 Authorization 헤더가 기본 정책으로 제거되는 환경이 있습니다.

  • 내부망에서는 보안상 Authorization 전달 차단
  • proxy_set_header Authorization $http_authorization; 누락

이 경우 OpenAI에는 Authorization이 전달되지 않아 401이 나거나, 사내 게이트웨이가 자체적으로 403을 반환할 수 있습니다.

2 스트리밍(SSE)에서 401/403처럼 보이는 끊김

스트리밍은 중간 프록시가 버퍼링/타임아웃을 일으키면 클라이언트에서 “인증이 끊겼나?”처럼 보이는 장애로 오해하기 쉽습니다. 실제로는 499/502/timeout 계열일 수 있으니, 스트리밍 장애는 별도 관점에서 점검하세요: OpenAI Responses API 스트리밍 끊김 타임아웃 완전 복구 가이드


4단계 실전 트러블슈팅 플로우 10분 안에 끝내기

아래 순서대로 하면 “키 재발급 무한 루프”를 대부분 피할 수 있습니다.

  1. 응답 바디에 나온 에러 타입/메시지를 그대로 확보(마스킹 후 공유)
  2. URL이 api.openai.com(또는 의도한 호스트)인지 확인
  3. 런타임에서 OPENAI_API_KEY 길이 확인(0이면 배포 설정)
  4. Authorization: Bearer 형식과 공백/개행 여부 확인
  5. (프로젝트 스코프 사용 시) OpenAI-Project가 맞는 값인지 확인
  6. 로컬 키 vs 운영 키를 바꿔서 A/B 테스트
    • 로컬 키로 운영에서 성공 → 운영 키의 RBAC/프로젝트 문제
    • 운영 키로 로컬에서 실패 → 키 자체/프로젝트 스코프/정책 문제
  7. 특정 모델만 실패하면 모델 허용 정책/RBAC 확인
  8. 프록시가 있다면 Authorization 전달 설정 확인

Best Practice 운영에서 401/403을 예방하는 설계

1 키/프로젝트를 설정 파일로 분리하고 시작 시 검증

앱 부팅 시점에 다음을 검증하고, 실패하면 즉시 죽이는 게 운영에서 낫습니다.

  • OPENAI_API_KEY 존재
  • OPENAI_PROJECT 존재(필요한 경우)
import os

def require_env(name: str) -> str:
    v = os.getenv(name)
    if not v:
        raise RuntimeError(f"Missing env: {name}")
    return v

OPENAI_API_KEY = require_env("OPENAI_API_KEY")
# 프로젝트가 필요한 아키텍처라면
# OPENAI_PROJECT = require_env("OPENAI_PROJECT")

2 서비스 계정 키는 최소 권한 원칙으로, 대신 관측 가능성 강화

  • 운영 키는 프로젝트/모델 권한을 최소화
  • 대신 401/403 발생 시 원인을 바로 찾도록
    • 요청 대상 호스트
    • 프로젝트 식별자
    • 모델명
    • 에러 코드

를 구조화 로그로 남기세요(키는 절대 남기지 않기).

3 재시도는 401/403에 하지 말 것

401/403은 대부분 영구 오류입니다. 재시도는 장애를 키웁니다.

  • 429/5xx/네트워크 타임아웃만 재시도
  • 401/403은 즉시 알람 + 설정 점검 플로우로 전환

레이트리밋 대응은 아래 가이드가 도움이 됩니다: OpenAI API 429 폭탄 대응 실전 가이드 지수 백오프 큐잉 토큰 버짓으로 비용과 지연을 함께 줄이기


결론

OpenAI Responses API의 401/403은 “키가 틀렸나?”로 단순화하면 해결이 늦어집니다. 401은 키/헤더/엔드포인트/프록시 전달을, 403은 프로젝트 스코프와 RBAC, 모델/정책 권한을 우선 의심해야 합니다.

오늘 바로 할 일은 간단합니다.

  • 운영 환경에서 OPENAI_API_KEY/프로젝트 설정이 실제로 주입되는지 확인하고
  • 요청 URL/헤더를 마스킹 로깅으로 남기며
  • 401과 403을 분리한 체크리스트로 원인을 좁혀보세요.

이 3가지만 해도 “키 재발급 → 또 실패” 루프에서 빠르게 탈출할 수 있습니다.