You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Docker+Nginx环境下Certbot验证acme-challenge报404问题求助

Docker部署Django时ACME挑战路径404问题排查与解决

我在Ubuntu 24.04 LTS(GNU/Linux 6.8.0-36-generic x86_64)服务器上用Docker部署Django站点,docker-compose up --build执行后构建无报错,但访问/.well-known/acme-challenge/和/.well-known/acme-challenge/test-file/时出现404错误,目标是通过Let's Encrypt和Certbot配置HTTPS并实现证书自动续期。

错误日志

nginx_1    | 123.45.67.89 - - [06/Aug/2024:11:06:34 +0000] "GET /.well-known/acme-challenge/ HTTP/1.1" 404 555 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" "-"
nginx_1    | 2024/08/06 10:20:00 [error] 29#29: *12 "/var/www/allhoa/certbot/.well-known/acme-challenge/test-file/index.html" is not found (20: Not a directory), client: 123.45.67.891, server: mydjangosite.com, request: "GET /.well-known/acme-challenge/test-file/ HTTP/1.1", host: "www.mydjangosite.com"
nginx_1    | 123.45.67.891 - - [06/Aug/2024:10:20:00 +0000] "GET /.well-known/acme-challenge/test-file/ HTTP/1.1" 404 555 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" "-"
nginx_1    | 123.45.67.891 - - [06/Aug/2024:10:20:50 +0000] "GET / HTTP/1.1" 200 27800 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" "-"
web_1      | 172.18.0.6 - - [06/Aug/2024:10:20:50 +0000] "GET / HTTP/1.0" 200 27800 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"

相关配置文件

/var/www/mydjangosite/config/gunicorn.conf.py

import multiprocessing

bind = "0.0.0.0:9000"
workers = multiprocessing.cpu_count() * 2 + 1
loglevel = 'info'
errorlog = '-'
accesslog = '-'

/var/www/mydjangosite/config/nginx.conf

user nginx;
worker_processes auto;
pid /var/run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log /var/log/nginx/access.log main;
    error_log /var/log/nginx/error.log warn;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    server {
        listen 80;          # Listen on IPv4
        listen [::]:80;     # Listen on IPv6
        server_name mydjangosite.com www.mydjangosite.com;
    
        location /.well-known/acme-challenge/ {
            root /var/www/mydjangosite/config/www;
        }

         location / {
            proxy_pass http://web:9000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }

        location /static/ {
            alias /code/staticfiles/;
        }

        location /media/ {
            alias /code/media/;
        }
    }
}

/var/www/mydjangosite/docker-compose.yml

# version: '3.8' # not required

volumes:
  postgres_data: {}
  static_volume: {}
  # certbot-etc: {}
  # certbot-var: {}

services:

  nginx:
    image: nginx:latest
    env_file:
      - .env
    volumes:
      - ./config/nginx.conf:/etc/nginx/nginx.conf
      - ./config/certbot/conf/options-ssl-nginx.conf:/etc/letsencrypt/options-ssl-nginx.conf
      - ./config/certbot/conf/ssl-dhparams.pem:/etc/letsencrypt/ssl-dhparams.pem
      # - .:/code
      - static_volume:/code/staticfiles
      - ./config/certbot/conf:/etc/letsencrypt
      - ./config/certbot/www:/var/www/mydjangosite/config/certbot/www
    ports:
      - "80:80"
      - "443:443"
    depends_on:
      - web
    command: nginx -g 'daemon off;'

  certbot:
    image: certbot/certbot
    volumes:
      - ./config/certbot/conf:/etc/letsencrypt
      - ./config/certbot/www:/var/www/allhoa/config/certbot/www
      - ./certbot-entrypoint.sh:/certbot-entrypoint.sh
    entrypoint: ["/certbot-entrypoint.sh"]

  web:
    build:
      context: .
    command: >
      bash -c "
        cat .env &&
        pip install --default-timeout=100 --retries=5 -r requirements.txt &&
        python manage.py migrate &&
        python manage.py collectstatic --no-input &&
        gunicorn --config ./config/gunicorn.conf.py allhoa.wsgi:application"
    environment:
      - DATABASE_URL=postgres://${DBUSER}:${DBPASSWORD}@${DBHOST}:${DBPORT}/${DBNAME}
    env_file:
      - .env
    volumes:
      - .:/code
      - static_volume:/code/staticfiles
    ports:
      - "9000:9000"

/var/www/mydjangosite/Dockerfile

# Use an official Python runtime as a parent image
FROM python:latest

# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

# Set the working directory in the container
WORKDIR /code

# Copy the project code
COPY . /code/

# Install any needed packages specified in requirements.txt
RUN apt-get update && apt-get install -y libxslt-dev libxml2-dev libpam-dev libedit-dev && apt-get clean && rm -rf /var/lib/apt/lists/*
# RUN pip install --upgrade pip && pip install -r requirements.txt
RUN pip install --upgrade pip && pip install --default-timeout=100 --retries=5 -r requirements.txt
RUN python manage.py collectstatic --no-input

# Collect static files
RUN python manage.py collectstatic --no-input

# Command to run the Django application
CMD ["gunicorn", "mydjangosite.wsgi:application", "--bind", "0.0.0.0:9000"]

问题排查与修复

从错误日志和配置文件来看,核心问题是路径映射不匹配,导致Nginx找不到ACME挑战文件,以下是具体修复步骤:

1. 修正Nginx的ACME路径配置

Nginx配置中location /.well-known/acme-challenge/的root路径与docker-compose挂载路径不匹配,修改nginx.conf中的对应配置:

location /.well-known/acme-challenge/ {
    root /var/www/mydjangosite/config/certbot/www;
}

2. 修正Certbot的挂载路径拼写错误

Certbot容器挂载路径中的allhoa是拼写错误,需与Nginx容器内路径保持一致,修改docker-compose.yml中certbot服务的volumes:

certbot:
  image: certbot/certbot
  volumes:
    - ./config/certbot/conf:/etc/letsencrypt
    - ./config/certbot/www:/var/www/mydjangosite/config/certbot/www
    - ./certbot-entrypoint.sh:/certbot-entrypoint.sh
  entrypoint: ["/certbot-entrypoint.sh"]

3. 添加HTTPS监听配置

当前nginx.conf仅配置了80端口,需添加443端口的HTTPS配置块:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name mydjangosite.com www.mydjangosite.com;

    ssl_certificate /etc/letsencrypt/live/mydjangosite.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mydjangosite.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    location / {
        proxy_pass http://web:9000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /static/ {
        alias /code/staticfiles/;
    }

    location /media/ {
        alias /code/media/;
    }
}

# 证书颁发成功后,可添加HTTP强制跳转HTTPS配置
server {
    listen 80;
    listen [::]:80;
    server_name mydjangosite.com www.mydjangosite.com;
    
    # 保留ACME挑战路径,其余请求跳转HTTPS
    location /.well-known/acme-challenge/ {
        root /var/www/mydjangosite/config/certbot/www;
    }
    location / {
        return 301 https://$host$request_uri;
    }
}

4. 配置Certbot自动续期

确保certbot-entrypoint.sh包含续期逻辑,示例脚本如下:

#!/bin/sh
set -e

# 获取/续期证书
certbot certonly --webroot -w /var/www/mydjangosite/config/certbot/www -d mydjangosite.com -d www.mydjangosite.com --email your-email@example.com --agree-tos --non-interactive

# 续期后重启Nginx生效
docker kill -s HUP nginx_nginx_1

给脚本添加执行权限:chmod +x certbot-entrypoint.sh

5. 测试ACME路径有效性

修复配置后重启服务:docker-compose down && docker-compose up --build,创建测试文件验证路径:

echo "test-content" > ./config/certbot/www/.well-known/acme-challenge/test-file

访问http://www.mydjangosite.com/.well-known/acme-challenge/test-file,若能看到"test-content"则配置正确。

内容的提问来源于stack exchange,提问作者Alex Winkler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 03:29:53