배포한 파일은 대부분 그대로 서빙됩니다. 다만 **몇 개의 주소는 플랫폼이 먼저 가져갑니다.** 거기에 파일을 올리면 업로드는 성공하는데 열어 보면 404 입니다.

이 문서는 그 주소를 전부 적습니다. 피해서 만들면 됩니다.

> 판정 표기 — **플랫폼**: 배포한 파일이 절대 안 나갑니다 · **내 파일**: 배포본이 이깁니다

---

## 1. 파일이 도달하지 못하는 주소

| 주소 | 매칭 | 실제 응답 | 판정 |
|---|---|---|---|
| `/_next/…` | 접두 | 플랫폼 자산 또는 404 | 플랫폼 |
| `/api…` | 접두 (**경계 없음**) | 404 | 플랫폼 |
| `/_frozen/…` | 접두 | 플랫폼 자산 또는 404 | 플랫폼 |
| `/turnstile/frame` | 정확히 일치 | 스팸차단 위젯 프레임 | 플랫폼 |
| `/.well-known/oauth-protected-resource` 및 그 하위 | 접두 | OAuth 메타데이터 JSON | 플랫폼 |
| `/.well-known/…` (그 밖) | — | 배포본 서빙 | **내 파일** |

> ⚠️ **`/api` 는 경계 검사가 없습니다.** `/api-docs.html` · `/apiary/index.html` 처럼 `/api` 로 **시작만 해도** 함께 잡힙니다. `/_next` 도 같습니다(`/_nextdoor.html`). 최상위에 그 이름으로 시작하는 폴더·파일을 두지 마세요.

## 2. 사이트 밖으로 나가는 주소

| 주소 | 호스트 | 동작 | 판정 |
|---|---|---|---|
| `/sites/…` | `<주소>.saeroon.com` | 404 | 플랫폼 |
| `/sites/{a}/{b}` | 커스텀 도메인 | **301 → `https://{a}.saeroon.com/{b}`** | 플랫폼 |
| `/admin…` (**경계 없음**) | `<주소>.saeroon.com` | **301 → 대시보드** | 플랫폼 |
| `/admin…` | 커스텀 도메인 | 배포본 서빙 | **내 파일** |

> ⚠️ 서브도메인에서는 `/administration/` · `/adminx.html` 도 함께 301 됩니다. 그리고 이 동작은 **서브도메인에만** 있어 커스텀 도메인과 결과가 갈립니다.

## 3. 요청 주소와 파일 경로가 달라지는 규칙

| 요청 | 어떻게 되나 | 결과 |
|---|---|---|
| `/ko/…` `/en/…` `/ja/…` `/zh-CN/…` `/zh-TW/…` `/ar/…` `/ru/…` | 맨 앞 언어 코드가 **떨어져 나감** | `/en/index.html` → 루트 `index.html` 을 서빙. **`en/` 폴더는 어떤 주소로도 열 수 없습니다** |
| `/{32~64자 16진수·하이픈}.txt` | IndexNow 키 파일로 해석 | **항상 404** (그 처리기가 아직 없습니다) |
| `/foo.html` | 확장자 없는 주소로 **301** | `/foo` |
| `/foo/` | **308** | `/foo` |
| `/foo` (확장자 없음) | `foo/index.html` 을 찾음 | **확장자 없는 파일은 서빙할 수 없습니다** |

> 🔴 언어 코드는 **최상위 폴더 이름으로 쓰지 마세요.** 다국어 설정을 켜지 않아도 규칙이 적용됩니다.

## 4. 배포본이 이기는 주소 (오해 방지)

| 주소 | 동작 |
|---|---|
| `/robots.txt` | 올렸으면 그 파일. **없을 때만** 플랫폼이 자동 생성 |
| `/sitemap.xml` | 위와 같음 (자동 생성분은 `noindex` 페이지를 제외합니다) |
| `/llms.txt` | 올린 파일만. 없으면 404 (자동 생성 없음) |
| `/404.html` | 루트에 두면 없는 주소의 응답 본문으로 씁니다 |
| `/favicon.ico` | 올린 파일이 나갑니다 |

> `/favicon.ico` 는 2026-08-06 이전에는 배포해도 404 였습니다. 지금은 정상입니다. 그 전에 우회하려고 `favicon.svg` 만 걸어 두었다면 그대로 두어도 됩니다.

## 5. 예약된 쿼리 파라미터

플랫폼이 **덮어씁니다** — 같은 이름을 쓰면 내 값이 사라집니다.

`_locale` · `_whitelabel` · `_domain` · `_showLocaleBanner` · `_suggestedLocale` · `_bannerStyle`

읽기 전용으로 해석되는 것: `_draft=1` · `_preview=1` · `saeroon-banner`

## 6. 그 밖의 제약

- **GET 요청만** 받습니다. 사이트 주소로 보낸 POST·PUT·DELETE 는 405 입니다. 폼 제출·API 호출은 사이트 주소가 아니라 API 주소로 보내세요.
- 사이트를 보관(Archived)하면 **모든 주소가 410** 이 됩니다.
- 서브도메인으로 쓸 수 없는 예약어 목록은 별개입니다 — 사이트를 만들 때 알려 드립니다.

---

## 7. 배포한 파일은 지워지지 않습니다

⚠️ **알고 있어야 할 동작입니다.**

배포는 **덮어쓰기만** 합니다. 지우지 않습니다.

폴더에서 파일을 지우고 다시 배포하면 새 목록에는 없지만, **그 주소는 계속 열려 있습니다.** 한 번 올라간 파일을 공개 통로로 회수하는 방법은 현재 없습니다.

```bash
# 이렇게 해도 https://<주소>.saeroon.com/assets/hero-b.jpg 는 계속 200 입니다
rm assets/hero-b.jpg
npx @saeroon/cli deploy .
```

**그래서 이렇게 하세요.**

- 배포 폴더에는 **공개해도 되는 것만** 둡니다. 검수용 캡처·내부 문서·고객 자료를 잠깐 넣었다 빼는 습관을 만들지 마세요.
- 비밀 값(API 키·비밀번호·토큰)이 파일에 들어갔다면 **이미 유출된 것으로 보고 재발급**하세요. CLI 가 그런 파일을 업로드에서 빼 주지만, **전에 한 번이라도 배포했다면 그 주소는 그대로 열려 있습니다.**
- 배포 전 폴더를 한 번 훑으세요. `find . -type f | sort` 로 충분합니다.

사이트를 통째로 지우는 것(`site archive` → `site delete`) 말고는 개별 파일을 없애는 명령이 없습니다. 파일 단위 정리 통로는 검토 중입니다.

---

## 이 표가 낡지 않게 하는 것

각 행은 코드의 회귀 테스트와 짝지어 있습니다 — `middleware/matcher.test.ts` · `middleware/routing.test.ts` · `static-sites/[slug]/[[...path]]/r2-path.test.ts`. 동작이 바뀌면 그쪽이 먼저 깨집니다.

빠진 것을 발견하면 알려 주세요. 이 목록에 없는데 파일이 안 나가는 주소가 있다면 그건 결함입니다.
