Durch einige Sicherheitslücken in PHP und Node.js werden wir unser Webspace Server auf die aktuellste Version Updaten. In der zeit kann es zu ausfällen kommen!
← Back to all tutorials

Nginx reverse proxy with SSL

Secure multiple web services behind an Nginx reverse proxy with automatic Let's Encrypt SSL certificates – entirely in Docker.

Requirements

Step 1: Install Docker

# Install Docker (official method)
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# Log out and back in so the group change takes effect

# Check the Docker Compose plugin
docker compose version

Step 2: Create the project structure

mkdir -p ~/reverse-proxy/{nginx/conf.d,certbot/{conf,www}}
cd ~/reverse-proxy

Step 3: Create the Docker Compose file

# docker-compose.yml
services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./certbot/conf:/etc/letsencrypt
      - ./certbot/www:/var/www/certbot
    restart: unless-stopped

  certbot:
    image: certbot/certbot
    volumes:
      - ./certbot/conf:/etc/letsencrypt
      - ./certbot/www:/var/www/certbot
    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h; done'"

Step 4: HTTP-only configuration for the ACME challenge

Nginx refuses to start if a configuration points to certificate files that do not exist yet. So start with port 80 only – this is enough for Let's Encrypt to verify your domain:

# nginx/conf.d/default.conf
server {
    listen 80;
    server_name deine-domain.de;

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

    location / {
        return 200 'ok';
    }
}
# Start Nginx (port 80 only for now)
docker compose up -d nginx

# Check: should return "ok"
curl http://deine-domain.de

Step 5: Request the SSL certificate

# Issue the certificate via the webroot of the running Nginx
docker compose run --rm --entrypoint certbot certbot certonly \
  --webroot -w /var/www/certbot \
  -d deine-domain.de \
  --email deine@email.de \
  --agree-tos --no-eff-email

# Check: the certificate files now exist
sudo ls certbot/conf/live/deine-domain.de/

--entrypoint certbot is required because the Compose file replaces the entrypoint of the certbot service with the renewal loop.

Step 6: Enable HTTPS and the reverse proxy

Only now that the certificate exists, replace default.conf with the full configuration. Replace dein-dienst:3000 with the service name and port of your app (the container must run in the same Compose project or Docker network).

# nginx/conf.d/default.conf
server {
    listen 80;
    server_name deine-domain.de;

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

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name deine-domain.de;

    ssl_certificate /etc/letsencrypt/live/deine-domain.de/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/deine-domain.de/privkey.pem;

    location / {
        proxy_pass http://dein-dienst:3000;
        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;
    }
}
# Test the configuration, then reload Nginx
docker compose exec nginx nginx -t
docker compose exec nginx nginx -s reload

# Start the certbot container for automatic renewal
docker compose up -d certbot

The certbot container checks every 12 hours whether the certificate needs renewing. Nginx only uses a renewed certificate after a reload – so run the reload command above regularly, e.g. weekly via cron.

Tip: With a reverse proxy, you can run as many services as you like on one server – each with its own SSL certificate. Ideal for KVM servers, where you have full control over ports and networking.

Multiple projects on one server?

Our KVM servers come with static IPs and full root access – perfect for reverse proxy setups.

Order a server