DevOps

Docker compose: nginx and wordpress behind Cloudflare — HTTPS to HTTP proxying problem

Faced an unusual issue — docker wordpress image fails to work with Cloudflare out of the box.

The problem is that I originally had Flexible proxying configured instead of Full (strict) on Cloudflare. In this case, the request goes to Cloudflare via HTTPS, but from Cloudflare to server via HTTP.

As a result, I decided not to change docker-compose-yml with additional custom image assemblies and just to connect certbot without installing it onto the docker host.

So the solution is to create two folders on the host machine /data/certbot/letsencrypt for certificates and /data/certbot/www to store acme challenges from letsencrypt. Then I’ve just mounted them on the Nginx host. Here is my nginx host config as is:

nginx:
  image: nginx:1.21-alpine
  restart: unless-stopped
  volumes:
    - /data/log/nginx:/var/log/nginx
    - ./sites-enabled:/etc/nginx/sites-enabled
    - ./nginx.conf:/etc/nginx/nginx.conf
    - /data/certbot/letsencrypt:/etc/letsencrypt
    - /data/certbot/www:/var/www/certbot
    - ./127.ru:/var/www/127.ru # here goes wordpress
  ports:
    - 80:80
    - 443:443
  depends_on:
    - mysql
    - wordpress

In the nginx config at sites-enabled/127.ru

server {
  listen 80;
  server_name     127.ru;
  index index.php;
  root /var/www/127.ru;
  location /.well-known/acme-challenge/ {
    root /var/www/certbot;
  }
  location / {
    try_files $uri $uri/ /index.php;
  }
  location ~ ^/.+.php(/|$) {
    try_files $uri =404;
    include /etc/nginx/fastcgi.conf;
    fastcgi_pass wp127ru:9000;
  }
  #listen 443 ssl;
  #ssl_certificate /etc/letsencrypt/live/127.ru/fullchain.pem;
  #ssl_certificate_key /etc/letsencrypt/live/127.ru/privkey.pem;
}

Turn off port 80 later - it is useless. The important line here is /.well-known/acme-challenge/ - at this path certbot will check the acme-challenge when generating an SSL certificate.

Then, from the host machine with Docker, we obtain the certificate to the mounted folders:

docker run --rm --name temp_certbot \
-v /data/certbot/letsencrypt:/etc/letsencrypt \
-v /data/certbot/www:/tmp/letsencrypt \
certbot/certbot \
certonly --webroot --agree-tos --renew-by-default \
--preferred-challenges http-01 --server https://acme-v02.api.letsencrypt.org/directory \
--text --email my@email.com \
-w /tmp/letsencrypt -d 127.ru

Now just remove port 80 in the nginx config (line «listen 80;») and uncomment the lines off SSL certs in the same place:

#listen 443 ssl;
#ssl_certificate /etc/letsencrypt/live/127.ru/fullchain.pem;
#ssl_certificate_key /etc/letsencrypt/live/127.ru/privkey.pem;

Next s witch Cloudflare to full proxying.

Now just restart nginx:

sudo docker exec -ti your-docker-host sh -c "nginx -s reload"

Categories