# Could not bind TCP port 80：Certbot 端口被占用的修法

> Certbot 的 standalone 插件要自己监听 80 端口，Nginx、宝塔等已经占用 80 或没用 root 运行时就会报这个错。改用 webroot/nginx 插件，或者用钩子临时停服务。

Source: https://ssl.sh/zh/errors/certbot-could-not-bind-tcp-port-80/
Last updated: 2026-11-10

**“Could not bind TCP port 80” 的意思是：Certbot 的 standalone 插件想在 80 端口上为 HTTP-01 验证启动自己的临时 Web 服务，被操作系统拒绝了。** 要么是别的程序（通常是你的 Web 服务器）已经在监听 80 端口，要么是 Certbot 没有以足够的权限运行，开不了 1024 以下的端口。域名和 Let's Encrypt 都没问题，要在本机解决。

## 报错的两种写法

这条报错有两种后半句，分别对应端口被占用和权限不够。Certbot 根据系统返回的套接字错误选择措辞：

```text
Could not bind TCP port 80 because it is already in use by another process on this
system (such as a web server). Please stop the program in question and then try again.
```

```text
Could not bind TCP port 80 because you don't have the appropriate permissions (for
example, you aren't running this program as root).
```

第一种在交互运行时还会询问 Retry 或 Cancel。日志里可能还有底层的 `Problem binding to port 80: ...`。

## 为什么非得是 80 端口

Let's Encrypt 的 HTTP-01 验证只连 80 端口。CA 会访问 `http://example.com/.well-known/acme-challenge/<token>`，Let's Encrypt 文档写明 HTTP-01 只能走 80 端口。standalone 插件用自己的服务来回应这个请求，所以 Certbot 文档说它需要绑定 80 端口，“可能需要先停掉现有的 Web 服务器”。

## 一般在什么场景下遇到

主要有三种场景：首次安装、自动续期和容器环境。第一次安装时，照着某篇教程执行了 `certbot certonly --standalone`，而服务器上早就跑着 Nginx 或宝塔。自动续期时，systemd 定时器或 cron 按几个月前保存的 standalone 设置执行 `certbot renew`，失败了也没人知道，直到证书快过期才暴露；Let's Encrypt 从 2025-06-04 起不再发到期提醒邮件，没有任何东西会提醒你。容器环境里，Certbot 跑在一个容器里，另一个反向代理容器已经把宿主机的 80 端口占了。

## 常见原因（按出现频率排序）

端口绑定失败有 5 种原因，最常见的是 Web 服务器已经占着 80 端口。

### 1. Web 服务器正在运行

Nginx、Apache、Caddy，或者宝塔、1Panel 等面板自带的 Web 服务占着 80 端口。查是谁：

```bash
sudo ss -ltnp 'sport = :80'
```

### 2. 续期配置里存的是 standalone

第一张证书是在还没装 Web 服务器时签的。Certbot 把验证方式记在 `/etc/letsencrypt/renewal/example.com.conf` 里，几个月后续期时 80 端口已经被占。检查：

```bash
sudo grep -E 'authenticator|pre_hook|post_hook' /etc/letsencrypt/renewal/example.com.conf
```

### 3. 容器映射了 80 端口

`docker ps` 里能看到反向代理容器有 `0.0.0.0:80->80/tcp`，宿主机上的 Certbot 就绑不了同一个端口。改用 webroot 的做法见 [Docker + Nginx + Certbot](https://ssl.sh/zh/guides/docker-nginx-certbot/)。

### 4. 没有用 root 运行

用普通用户运行，或者在没有绑定低端口权限的容器里运行，会出现权限那种写法。加 `sudo`，或者在有权限的环境里运行。

### 5. 上一次运行卡住了

被中断的 Certbot 进程可能还占着端口。`ss` 里能看到 `certbot` 或 `python` 进程，结束它再重试。

## 怎么修

**方案 A：别用 standalone，改用现有的 Web 服务器（推荐）。**

装了 Nginx 或 Apache 的，直接让 Certbot 用它们：

```bash
sudo certbot certonly --nginx -d example.com -d www.example.com
```

或者让 Web 服务器继续运行，用 webroot 方式，从网站目录提供验证文件：

```bash
sudo certbot certonly --webroot -w /var/www/example.com -d example.com -d www.example.com
```

已有证书想换验证方式，Certbot 2.3.0 及以上版本提供 `reconfigure`：它会先用新设置对测试服务器做一次续期演练，成功后才保存：

```bash
sudo certbot reconfigure --cert-name example.com --webroot -w /var/www/example.com
```

宝塔面板用户不需要另装 Certbot：面板“网站 → 设置 → SSL”里自带 Let's Encrypt 申请和续签，支持文件验证和 DNS 验证，文件验证通过网站目录提供验证文件，不会和面板的 Nginx 抢端口，详见 [宝塔面板配置 SSL](https://ssl.sh/zh/guides/bt-panel-ssl/)。

**方案 B：保留 standalone，续期时临时停一下 Web 服务。**

```bash
sudo certbot renew --pre-hook "systemctl stop nginx" --post-hook "systemctl start nginx"
```

Certbot 文档说明这两个钩子会在每次续期尝试的前后执行。放在 `/etc/letsencrypt/renewal-hooks/pre` 和 `/post` 目录里的可执行文件也会自动运行，见 [Certbot 续期钩子](https://ssl.sh/zh/guides/certbot-renewal-hooks/)。

面板环境下，宝塔、1Panel 等面板安装的 Nginx，服务名和启停方式可能和系统包管理器装的不一样，钩子里的命令要按实际情况写，并先手动执行一遍确认能停能起。面板环境更省事的做法是直接用面板自带的证书功能。

**方案 C：改用 DNS-01。** 不需要任何入站端口，还能申请通配符证书。

端口绑定的问题解决之后，如果验证又报连接超时，说明外部访问不到你的 80 端口，常见于云服务器安全组或防火墙没放行，见 [Certbot Timeout during connect](https://ssl.sh/zh/errors/certbot-timeout-during-connect/)。

**权限那种情况：** 用 `sudo` 运行，不要为此修改系统的端口权限。

## 用 acme.sh 时的同类问题

acme.sh 的 `--standalone` 模式有同样的问题，修法也和 Certbot 一样。它会在 80 端口启动内置 Web 服务，端口被占用时报 `tcp port 80 is already used by` 加上占用进程的名字。修法：换成 `--webroot` 或 `--nginx` 模式，让现有的 Web 服务器提供验证文件；或者干脆改用 DNS API 验证，用 DNSPod、阿里云 DNS 的接口自动添加 TXT 记录。其他验证失败的情况见 [acme.sh 验证报错](https://ssl.sh/zh/errors/acme-sh-verify-error/)。

## 不要这样做

这 4 种做法能绕过报错，但会误停别的网站、让验证继续失败，或者留下权限隐患。

- **不要不看清楚就结束占用 80 端口的进程。** 在共享服务器或面板环境里，它可能还承载着别的网站，应该通过服务管理器停止，才能干净地恢复。
- **不要随便改用 `--http-01-port`**，除非外部 80 端口转发到了这个端口，CA 永远只连 80。
- **不要为了不用 root 而给 Python 解释器开放绑定低端口的权限**，这个权限会对所有使用同一解释器的脚本生效。
- **不要为了消除报错而关掉自动续期。** 先把验证方式改对，再用演练确认。

## 怎么确认已经修好

修好后，演练命令 `certbot renew --dry-run` 会通过，`certbot certificates` 会显示新的到期时间。

1. 用测试环境演练整个流程，不影响现有证书：

   ```bash
   sudo certbot renew --dry-run
   ```

2. 正式运行后，`sudo certbot certificates` 能看到新的到期时间。
3. 确认 Web 服务已经恢复运行（`systemctl status nginx`），并用 [SSL 检测](https://ssl.sh/zh/tools/ssl-checker/) 确认网站用上了新证书。
