# HTTP 跳转 HTTPS：Nginx、Apache、宝塔、IIS 和 CDN 配置

> 用一次 301/308 跳转强制 HTTPS：Nginx、Apache、Caddy、IIS、宝塔和阿里云/腾讯云 CDN 写法，避免套 CDN 后无限跳转，并保证 ACME 续期不受影响。

Source: https://ssl.sh/zh/guides/redirect-http-to-https/
Last updated: 2026-11-10

HTTP 跳转 HTTPS 的标准做法，是对每个 `http://` 请求返回一次 301 或 308 永久跳转，直接到达对应的 `https://` 地址。套了 CDN 时要在 CDN 上跳转或判断 `X-Forwarded-Proto`，否则会无限跳转。Let's Encrypt 续期不受跳转影响。覆盖 Nginx、Apache 2.4、Caddy 2、IIS 10、宝塔面板，以及阿里云、腾讯云、Cloudflare 等 CDN。

## 前提条件

强制 HTTPS 之前，HTTPS 本身必须已经正常。

- 已安装有效证书，`https://example.com` 打开没有任何警告（用 [SSL 证书检查](https://ssl.sh/zh/tools/ssl-checker/) 确认）。
- 能修改 Web 服务器或 CDN 的配置。
- 页面里没有写死的 `http://` 资源，可以用 [混合内容扫描](https://ssl.sh/zh/tools/mixed-content-scanner/) 查找。

## 第一步：Nginx

Nginx 用一个只做跳转的 80 端口 server 块，把请求 301 到同名的 HTTPS 地址：

```nginx
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://example.com$request_uri;
}
```

写固定域名可以一次把 `www` 和主域名统一；想保留访问的域名就写 `https://$host$request_uri`。然后执行 `sudo nginx -t && sudo systemctl reload nginx`。

## 第二步：Apache

Apache 在 80 端口的虚拟主机里用 `Redirect permanent` 跳转：

```apache
<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    Redirect permanent / https://example.com/
</VirtualHost>
```

只能用 `.htaccess`（虚拟主机）时用 mod_rewrite：

```apache
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
```

## 第三步：Caddy、IIS 和宝塔

Caddy 对所有写了域名的站点自动做 HTTP 跳转，不需要配置。宝塔在网站 → 设置 → SSL 里打开“强制 HTTPS”。IIS 先安装 URL Rewrite 模块，在 `web.config` 里加规则：

```xml
<rule name="HTTP to HTTPS" stopProcessing="true">
  <match url="(.*)" />
  <conditions>
    <add input="{HTTPS}" pattern="^OFF$" />
  </conditions>
  <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" />
</rule>
```

## 第四步：套了 CDN 或负载均衡时

CDN 以 HTTP 回源时，源站看到的每个请求都是 HTTP，源站再跳转就会死循环。两种做法：在 CDN 上开启强制跳转（阿里云、腾讯云 CDN 的“强制跳转”，Cloudflare 的 **Always Use HTTPS**）并去掉源站的跳转；或者只在代理告诉你客户端用的是 HTTP 时才跳转：

```nginx
if ($http_x_forwarded_proto = "http") {
    return 301 https://$host$request_uri;
}
```

更好的做法是让 CDN 用 HTTPS 回源并校验证书。阿里云 CDN 的设置见 [阿里云 CDN 配置 HTTPS](https://ssl.sh/zh/guides/aliyun-cdn-https/)。

## 验证

配置正确时，每个 `http://` 入口只跳转一次，直接到达最终的 `https://` 地址。用 curl 逐个检查入口：

```bash
curl -sI http://example.com | grep -iE '^(HTTP|location)'
curl -sI http://www.example.com | grep -iE '^(HTTP|location)'
curl -sIL http://www.example.com | grep -iE '^(HTTP|location)'
```

再用 [SSL 证书检查](https://ssl.sh/zh/tools/ssl-checker/) 检查 HTTPS 本身，或在终端运行：

```bash
curl ssl.sh/example.com
```

## 自动续期

跳转不影响 Let's Encrypt 续期：HTTP-01 验证会跟随最多 10 次跳转（只限 80/443 端口的 http/https），而且不校验 HTTPS 目标的证书，即使证书已过期也不影响续期。会破坏 HTTP-01 的是跳到另一个不提供验证文件的域名或端口，或者删掉了 80 端口的监听。保留 80 端口的 server 块，让 ACME 客户端照常续期（见 [证书自动续期](https://ssl.sh/zh/guides/renew-certificates-automatically/)）。跳转正常后，再加上 [HSTS](https://ssl.sh/zh/guides/enable-hsts/)。

## 常见问题排查

- **ERR_TOO_MANY_REDIRECTS（重定向次数过多）**：CDN 已经用 HTTPS 接收请求，源站又按 HTTP 跳转，见第四步；宝塔“强制 HTTPS”和 CDN 跳转同时开也可能出现。
- **切换后提示混合内容**：页面还在加载 `http://` 的脚本或图片，见 [混合内容警告](https://ssl.sh/zh/errors/mixed-content-warning/)。
- **跳转后浏览器提示证书错误**：证书不包含跳转目标域名，见 [您的连接不是私密连接](https://ssl.sh/zh/errors/your-connection-is-not-private/)。
- **表单或 API 出错**：301 把 POST 变成了 GET，API 改用 308。
- **加跳转后续期失败**：跳到了别的域名或端口，见 [certbot 连接超时](https://ssl.sh/zh/errors/certbot-timeout-during-connect/)。
