# 【常見問題】https 使用domain 與 ip 差異

好的，以下補上：

---

## ✅ 比較 `https://a.b.com` 與 `https://168.23.1.1` 的差異

這兩者**協定一樣**（都是 HTTPS），但**主機名稱（Host）差異會影響 SSL 憑證驗證、DNS 解析、與 Nginx 的設定行為**。

---

##  差異對照表

<table id="bkmrk-%E9%A0%85%E7%9B%AE-https%3A%2F%2Fa.b.com-h"><thead><tr><th>項目</th><th>`https://a.b.com`</th><th>`https://168.23.1.1`</th></tr></thead><tbody><tr><td>**協定**</td><td>HTTPS（加密）</td><td>HTTPS（加密）</td></tr><tr><td>**主機名稱**</td><td>網域名稱（FQDN）</td><td>IP 位址</td></tr><tr><td>**DNS 解析**</td><td>會經過 DNS 解析 → IP</td><td>不經 DNS</td></tr><tr><td>**SSL 憑證驗證**</td><td>通常需要**憑證中的 CN/SAN 包含 a.b.com**</td><td>需要**憑證中包含 IP 位址**（不常見）</td></tr><tr><td>**Nginx `server_name`** 配置</td><td>`server_name a.b.com;`</td><td>`server_name 168.23.1.1;`（不建議）或 `_`</td></tr><tr><td>**常見用法**</td><td>公網、正式站、內部網域皆可</td><td>測試、開發、內網 IP 使用</td></tr><tr><td>**風險與限制**</td><td>SSL 憑證驗證容易通過</td><td>一般憑證不支援 IP → 會出現 TLS 錯誤</td></tr><tr><td>**信任憑證**</td><td>可用 Let's Encrypt、商業憑證</td><td>除非特別申請 IP 憑證，否則會有警告</td></tr></tbody></table>

---

##  SSL 憑證驗證差異舉例

### `https://a.b.com`

- 若你使用 Let's Encrypt 為 `a.b.com` 建立憑證：  
    ✅ `curl https://a.b.com` ➜ 成功（CN/SAN 包含 `a.b.com`）

### `https://168.23.1.1`

- 若你拿 `a.b.com` 憑證套用到 IP 上：  
    ❌ `curl https://168.23.1.1` ➜ 失敗，出現 `SSL: certificate subject name does not match`

###  解法（不建議用於正式環境）：

- 為 IP 簽發 SSL 憑證（需自簽或企業憑證）
- 用 `curl -k` 忽略憑證錯誤

```bash
curl -k https://168.23.1.1  # 跳過憑證驗證（不安全）

```

---

## ✅ Nginx 設定差異

### `a.b.com` 對應設定：

```nginx
server {
    listen 443 ssl;
    server_name a.b.com;

    ssl_certificate     /etc/nginx/certs/a.b.com.crt;
    ssl_certificate_key /etc/nginx/certs/a.b.com.key;

    location / {
        proxy_pass http://localhost:3000;
    }
}

```

---

### IP 直接連線時，通常會用：

```nginx
server {
    listen 443 ssl;
    server_name _;  # 匹配所有 hostname（或不設定）

    ssl_certificate     /etc/nginx/certs/selfsigned.crt;
    ssl_certificate_key /etc/nginx/certs/selfsigned.key;

    location / {
        proxy_pass http://localhost:3000;
    }
}

```

> 🧨 若直接用 IP，也許不會觸發 `server_name` 對應，取決於 Nginx 設定順序與 SNI。

---

## ✅ 小結

<table id="bkmrk-url-%E9%81%A9%E7%94%A8%E6%99%82%E6%A9%9F-%E6%86%91%E8%AD%89%E9%9C%80%E6%B1%82-%E9%A2%A8%E9%9A%AA-htt"><thead><tr><th>URL</th><th>適用時機</th><th>憑證需求</th><th>風險</th></tr></thead><tbody><tr><td>`https://a.b.com`</td><td>公網、正常使用</td><td>CN/SAN 需含 `a.b.com`</td><td>正常通過憑證驗證</td></tr><tr><td>`https://168.23.1.1`</td><td>內部測試或特例</td><td>憑證需含 IP 或自簽</td><td>容易驗證失敗、跳警告</td></tr></tbody></table>

---

##  

## ✅Curl Domain 與 ip 的 差異

這是一個非常關鍵的細節問題，涉及到 **HTTPS、Host Header、SNI（Server Name Indication）**，我們來完整拆解：

---

## 你提供的兩個請求：

```bash
curl https://a.b.com

```

```bash
curl https://168.23.1.1 -H 'Host: a.b.com'

```

看起來類似，實際上在底層**行為非常不同**：

---

##  核心差異一覽

<table id="bkmrk-%E9%A0%85%E7%9B%AE-curl-https%3A%2F%2Fa.b."><thead><tr><th>項目</th><th>`curl https://a.b.com`</th><th>`curl https://168.23.1.1 -H 'Host: a.b.com'`</th></tr></thead><tbody><tr><td>**連線 IP**</td><td>DNS 解析 `a.b.com` → IP</td><td>直接指定 IP `168.23.1.1`</td></tr><tr><td>**TLS SNI 傳送內容**</td><td>`a.b.com`（🔑 用來選擇憑證）</td><td>`168.23.1.1`（🚨 TLS 層不包含 `a.b.com`）</td></tr><tr><td>**Host Header（HTTP 層）**</td><td>`Host: a.b.com`（由 curl 自動加）</td><td>`Host: a.b.com`（你手動加）</td></tr><tr><td>**SSL 憑證是否驗證成功？**</td><td>✅ 通常會成功</td><td>❌ 若憑證是發給 `a.b.com` 則會 mismatch</td></tr><tr><td>**Nginx 是否能進到正確的 server**</td><td>✅ 通常會進到 `server_name a.b.com`</td><td>❌ 取決於 SNI，可能進入 default server</td></tr></tbody></table>

---

##  為何 TLS 憑證驗證會失敗？

###  SNI 是什麼？

- TLS 層在握手階段會送出 `Server Name`（來自 `curl` 的 `--resolve` 或 URL）。
- 用來讓伺服器知道要回哪一張憑證。

### 在兩種情況下比較：

#### ✅ `curl https://a.b.com`：

- DNS → 168.23.1.1
- SNI: `a.b.com`
- Host header: `a.b.com`
- Nginx 匹配成功，憑證也是給 `a.b.com`，一切正常

#### ❌ `curl https://168.23.1.1 -H 'Host: a.b.com'`：

- SNI: `168.23.1.1`（預設值，非 a.b.com）
- Host header: `a.b.com`（只有 HTTP 層有用）
- 若 Nginx 用 `server_name a.b.com` 並綁定憑證，那會 **因為 SNI 不符無法選中正確的憑證 → TLS 握手失敗**

---

## Nginx 對這兩種請求的反應

```nginx
server {
    listen 443 ssl;
    server_name a.b.com;

    ssl_certificate     /etc/nginx/certs/a.b.com.crt;
    ssl_certificate_key /etc/nginx/certs/a.b.com.key;

    location / {
        return 200 "hello a.b.com\n";
    }
}

server {
    listen 443 ssl default_server;
    server_name _;

    ssl_certificate     /etc/nginx/certs/default.crt;
    ssl_certificate_key /etc/nginx/certs/default.key;

    location / {
        return 200 "default server\n";
    }
}

```

### 測試結果：

<table id="bkmrk-%E6%B8%AC%E8%A9%A6%E6%8C%87%E4%BB%A4-%E4%BC%BA%E6%9C%8D%E5%99%A8-block-%E5%91%BD%E4%B8%AD-%E5%9B%9E%E5%82%B3"><thead><tr><th>測試指令</th><th>伺服器 block 命中</th><th>回傳內容</th><th>是否 SSL 通過</th></tr></thead><tbody><tr><td>`curl https://a.b.com`</td><td>`server_name a.b.com`</td><td>hello a.b.com</td><td>✅ 是</td></tr><tr><td>`curl https://168.23.1.1 -H 'Host: a.b.com'`</td><td>`server_name _`（default）</td><td>default server</td><td>⚠️ 通常憑證不符（或需 `-k`）</td></tr></tbody></table>

---

## ✅ 解法：讓 IP 請求也能通過 SSL

### 方法一：用自簽或專門支援 IP 的憑證

但大多數公開 CA（如 Let's Encrypt）**不發給 IP 憑證**。

### 方法二：用 curl 忽略 TLS 驗證

```bash
curl -k https://168.23.1.1 -H 'Host: a.b.com'

```

### 方法三：讓 curl 傳送正確的 SNI（推薦）

```bash
curl --resolve a.b.com:443:168.23.1.1 https://a.b.com

```

- 這會讓 curl：
    
    
    - SNI 傳 `a.b.com`
    - Host header 是 `a.b.com`
    - 實際連到 IP `168.23.1.1`
- ✅ 完整模擬瀏覽器行為

---

## ✅ 總結

<table id="bkmrk-%E6%AF%94%E8%BC%83%E9%A0%85%E7%9B%AE-curl-https%3A%2F%2Fa."><thead><tr><th>比較項目</th><th>`curl https://a.b.com`</th><th>`curl https://168.23.1.1 -H 'Host: a.b.com'`</th></tr></thead><tbody><tr><td>DNS 解析</td><td>✅ 有</td><td>❌ 無</td></tr><tr><td>TLS SNI</td><td>`a.b.com`</td><td>`168.23.1.1`</td></tr><tr><td>Host Header</td><td>`a.b.com`</td><td>`a.b.com`</td></tr><tr><td>SSL 憑證驗證</td><td>✅ 通常成功</td><td>❌ 會失敗（除非用 `-k`）</td></tr><tr><td>建議用法</td><td>✅ 正常連線方式</td><td>⚠️ 用於測試或模擬</td></tr></tbody></table>

---

如果你需要模擬真實請求又繞過 DNS，可使用：

```bash
curl --resolve a.b.com:443:168.23.1.1 https://a.b.com

```

這是最穩定且最貼近瀏覽器 SNI 行為的方式。