目录

NGINX ACME 模块全攻略:从编译安装到自动化证书管理

基于 Rust 的官方 NGINX ACME 动态模块集成指南

ngx_http_acme_module 是由 NGINX 官方(F5, Inc.)开发的一个基于 Rust 的动态模块。它实现了 ACMEv2 协议(RFC 8555),允许 NGINX 直接处理证书的申请、续期和管理,支持 HTTP-01 和 TLS-ALPN-01 验证方式。

1. 编译与安装

由于该模块通常不包含在预编译的 NGINX 发行版中,我们需要通过源码编译的方式将其作为动态模块集成。

前置条件

  • 操作系统:推荐 Debian 或 Ubuntu。
  • 工具依赖:系统需安装有 curl, jq, tar 以及基本的编译环境。
  • 权限:具有 sudo 权限。

使用自动化脚本编译

你可以创建一个名为 nginx-acme.sh 的脚本并运行它。该脚本会自动执行以下流程:

  1. 获取 nginx-acme 最新版本。
  2. 检测系统当前 Nginx 版本并下载匹配的源码。
  3. 自动安装必要的编译依赖(Debian/Ubuntu)。
  4. 使用 --with-compat 参数编译动态模块,确保二进制兼容性。
  5. ngx_http_acme_module.so 安装到 Nginx 模块目录并验证加载。

脚本内容如下:

#!/usr/bin/env bash

#============================================================
# File: nginx-acme.sh
# Description: 编译 nginx-acme 模块并集成到当前系统 Nginx
# Author: Jetsung Chan <i@jetsung.com>
# Version: 0.1.0
# CreatedAt: 2026-03-15
# UpdatedAt: 2026-03-15
#============================================================

if [[ -n "${DEBUG:-}" ]]; then
    set -eux
else
    set -euo pipefail
fi

USER_ID="$(id -u)"

sudo_exec() {
    if [[ "$USER_ID" -ne 0 ]]; then
        sudo "$@"
    else
        "$@"
    fi
}

check_is_command() {
    command -v "$1" >/dev/null 2>&1
}

# 1. 获取最新 nginx-acme 版本和下载链接
echo "正在获取 nginx-acme 最新版本信息..."
ACME_RELEASE_INFO=$(curl -fsSL https://api.github.com/repos/nginx/nginx-acme/releases/latest)
ACME_VERSION=$(echo "$ACME_RELEASE_INFO" | jq -r '.tag_name')
ACME_DOWNLOAD_URL=$(echo "$ACME_RELEASE_INFO" | jq -r '.assets[] | select(.name | endswith(".tar.gz")) | .browser_download_url')

echo "最新版本: $ACME_VERSION"

# 2. 获取当前系统 Nginx 版本
if ! check_is_command "nginx"; then
    echo "错误: 系统未安装 nginx,无法编译模块。"
    exit 1
fi

NGINX_VER=$(nginx -v 2>&1 | cut -d '/' -f 2)
echo "当前 Nginx 版本: $NGINX_VER"

# 3. 准备工作目录
WORK_DIR=$(mktemp -d /tmp/nginx-acme-build.XXXXXX)
cd "$WORK_DIR"

# 4. 下载源码
echo "正在下载 nginx-acme 源码..."
curl -fsSL "$ACME_DOWNLOAD_URL" -o "nginx-acme.tar.gz"
tar -xzf "nginx-acme.tar.gz"
ACME_SRC_DIR=$(find . -maxdepth 1 -type d -name "nginx-acme-*" | head -n 1)

echo "正在下载 Nginx $NGINX_VER 源码..."
curl -fsSL "https://nginx.org/download/nginx-${NGINX_VER}.tar.gz" -o "nginx.tar.gz"
tar -xzf "nginx.tar.gz"
NGINX_SRC_DIR="nginx-${NGINX_VER}"

# 5. 安装编译依赖
echo "正在安装编译依赖..."
sudo_exec apt-get update -y
sudo_exec apt-get install -y clang pkg-config libssl-dev libpcre2-dev zlib1g-dev libclang-dev

# 6. 编译模块
echo "开始编译模块..."
cd "$NGINX_SRC_DIR"

# 获取原始编译参数并添加动态模块支持
# 注意:必须包含 --with-compat 以确保二进制兼容
./configure --with-compat --with-http_ssl_module --add-dynamic-module="../$ACME_SRC_DIR"

make modules

# 7. 安装模块
MODULE_PATH=$(nginx -V 2>&1 | grep -oP "modules-path=\K[^ ]*")
if [[ -z "$MODULE_PATH" ]]; then
    MODULE_PATH="/usr/lib/nginx/modules"
fi

echo "编译完成。正在将模块复制到 $MODULE_PATH..."
sudo_exec mkdir -p "$MODULE_PATH"
sudo_exec cp objs/ngx_http_acme_module.so "$MODULE_PATH/"

# 8. 测试模块是否正常加载
echo "正在验证模块是否加载正常..."
TEST_CONF=$(mktemp /tmp/nginx-acme-test.XXXXXX.conf)
cat > "$TEST_CONF" <<EOF
error_log /tmp/nginx-acme-test-error.log;
pid /tmp/nginx-acme-test.pid;
load_module $MODULE_PATH/ngx_http_acme_module.so;
events {
    worker_connections 1024;
}
http {
    access_log /tmp/nginx-acme-test-access.log;
}
EOF

if sudo_exec nginx -t -c "$TEST_CONF" > /dev/null 2>&1; then
    echo "验证成功: 模块 $MODULE_PATH/ngx_http_acme_module.so 已成功加载并与当前 Nginx 兼容。"
else
    echo "验证失败: 模块加载出错。请检查错误日志: /tmp/nginx-acme-test-error.log"
    sudo_exec cat /tmp/nginx-acme-test-error.log
    rm -f "$TEST_CONF"
    exit 1
fi

rm -f "$TEST_CONF" /tmp/nginx-acme-test-error.log /tmp/nginx-acme-test.pid /tmp/nginx-acme-test-access.log

echo "----------------------------------------------------"
echo "nginx-acme 模块已安装并验证成功!"
echo "模块位置: $MODULE_PATH/ngx_http_acme_module.so"
echo "请在您的主 nginx.conf 顶层添加以下行以启用模块:"
echo "load_module modules/ngx_http_acme_module.so;"
echo "----------------------------------------------------"

# 清理
rm -rf "$WORK_DIR"

赋予执行权限并运行:

chmod +x nginx-acme.sh
./nginx-acme.sh

若看到“nginx-acme 模块已安装并验证成功!”的提示,说明安装已完成。


2. 模块加载

在 NGINX 中使用动态模块,必须在 nginx.conf最顶部(全局块)进行加载:

load_module modules/ngx_http_acme_module.so;

3. 基础配置示例

以下是一个使用 Let’s Encrypt 获取证书并在 443 端口使用的典型配置:

http {
    # 1. 必须配置解析器,用于解析 ACME 服务器域名
    resolver 8.8.8.8 1.1.1.1 valid=300s;
    resolver_timeout 5s;

    # 2. 定义共享内存区域(必填),用于同步证书状态
    # 1M 空间约可管理 50 个证书
    acme_shared_zone zone=ngx_acme_shared:1M;

    # 3. 定义 ACME 发行者
    acme_issuer letsencrypt {
        uri https://acme-v02.api.letsencrypt.org/directory;
        state_path /var/cache/nginx/acme; # 存储账户和证书的目录
        accept_terms_of_service;
        contact mailto:admin@example.com;
    }

    # 4. HTTP 验证 Server (80 端口)
    server {
        listen 80;
        server_name example.com;

        # 模块会自动处理 /.well-known/acme-challenge/
        location / {
            return 301 https://$host$request_uri;
        }
    }

    # 5. HTTPS Server (443 端口)
    server {
        listen 443 ssl;
        server_name example.com;

        # 关联发行者并请求证书
        acme_certificate letsencrypt;

        # 使用模块提供的动态变量加载证书和密钥
        ssl_certificate     $acme_certificate;
        ssl_certificate_key $acme_certificate_key;

        # 推荐:启用证书缓存
        ssl_certificate_cache max=10;

        location / {
            root /usr/share/nginx/html;
        }
    }
}

4. 指令详解

指令上下文说明
acme_issuerhttp定义证书发行者块。内含 uri (必填)、contactstate_path 等子指令。
acme_certificateserver为当前虚拟主机请求证书。格式:acme_certificate issuer_name [identifiers...];
acme_shared_zonehttp分配共享内存以在所有工作进程间同步证书数据。
state_pathacme_issuer证书和账户数据的持久化存储路径。必须对 Nginx 用户可见并有写权限。

5. 进阶用法

TLS-ALPN-01 验证

如果你无法开启 80 端口,可以使用 TLS-ALPN-01 方式(必须在 443 端口进行):

acme_issuer cloud_issuer {
    uri https://...;
    challenge tls-alpn-01;
    state_path /etc/nginx/acme;
}

server {
    listen 443 ssl;
    server_name example.com;
    acme_certificate cloud_issuer;
    ...
}

IP 证书支持

该模块支持根据 RFC 8738 申请 IP 证书。只需将 IP 地址放入 server_name 或作为 acme_certificate 的参数即可。

嵌入式变量

模块提供了两个核心变量,直接用于 ssl_certificate 系列指令:

  • $acme_certificate: 签发后的证书内容(PEM 格式)。
  • $acme_certificate_key: 对应的私钥内容。

6. 注意事项与维护

目录权限

在启动前,请务必手动创建状态目录并授予 Nginx 运行用户(如 www-data)读写权限:

sudo mkdir -p /var/cache/nginx/acme
sudo chown www-data:www-data /var/cache/nginx/acme

域名解析

必须在 http 块中配置 resolver,否则模块无法连接到 ACME 发行者服务器。

验证配置与重启

完成修改后,请先测试配置:

sudo nginx -t
sudo systemctl reload nginx

Nginx 会自动启动后台进程开始与 ACME 服务器通信。你可以查看 Nginx 错误日志以追踪申请进度。

故障排查

  • 验证失败:检查 80 端口是否被防火墙拦截,或者是否存在强制性的全局 301 跳转干扰了验证路径。
  • 二进制不兼容:如果手动编译后无法加载,请确保编译环境的 Nginx 源码版本与系统安装的版本完全一致。

7. 相关资源