SSL and HTTPS
HTTPS is opt-in. With NGINX_SSL_ENABLED=false (the default) all NGINX_SSL_* variables are ignored and the container serves plain HTTP only.
Your own certificate
Mount the certificate and key read-only at the default paths, or point the path variables at them.
docker run --rm -d -p 80:80 -p 443:443 --user root \
-e NGINX_SSL_ENABLED=true \
-e NGINX_SERVER_NAME=app.example.com \
-v /etc/letsencrypt/live/app.example.com/fullchain.pem:/etc/nginx/ssl/cert.pem:ro \
-v /etc/letsencrypt/live/app.example.com/privkey.pem:/etc/nginx/ssl/key.pem:ro \
-v $(pwd)/public:/var/www/html:ro \
adamrollogi/php-nginx:8.4
The entrypoint checks both files are readable before starting and exits with a clear message if not.
Self-signed certificate
For local development or behind a terminating load balancer:
docker run --rm -d -p 8080:8080 -p 8443:8443 \
-e NGINX_PORT=8080 \
-e NGINX_SSL_ENABLED=true \
-e NGINX_SSL_PORT=8443 \
-e NGINX_SSL_SELF_SIGNED=true \
-e NGINX_SSL_SELF_SIGNED_CN=app.local \
adamrollogi/php-nginx:8.4
A 2048-bit RSA certificate with CN and SAN set to NGINX_SSL_SELF_SIGNED_CN (plus IP:127.0.0.1) is generated into /etc/nginx/ssl/ on first start and reused afterwards. Mount that directory to keep it between containers.
The HTTP to HTTPS redirect
With NGINX_SSL_REDIRECT=true (the default), a second listener on NGINX_SSL_REDIRECT_PORT (which inherits NGINX_PORT) answers:
/healthwith200 OK, so load balancers keep probing over plain HTTP./.well-known/acme-challenge/from the document root, so Let's Encrypt HTTP-01 validation works.- everything else with a
301tohttps://<host>[:port]<uri>.
The host in that redirect is chosen in this order:
NGINX_SSL_REDIRECT_HOSTif set.- The first entry of
NGINX_SERVER_NAME, unless it is_, a wildcard or a regex. - The request's
Hostheader, with a warning logged at startup.
Host a client sends, which is an open redirect. Set NGINX_SERVER_NAME or NGINX_SSL_REDIRECT_HOST for anything internet-facing.The port is omitted from the redirect when NGINX_SSL_PORT is 443.
Set NGINX_SSL_REDIRECT=false to serve HTTPS only with no HTTP listener.
HSTS
Strict-Transport-Security: max-age=63072000; includeSubDomains is sent on HTTPS responses by default. Tune with NGINX_SSL_HSTS_MAX_AGE or disable with NGINX_SSL_HSTS=false. There is no preload directive; add it through a drop-in if you have submitted your domain.
TLS settings
| Variable | Default |
|---|---|
NGINX_SSL_PROTOCOLS | TLSv1.2 TLSv1.3 |
NGINX_SSL_CIPHERS | Mozilla intermediate list (ECDHE and DHE with AES-GCM and ChaCha20) |
NGINX_SSL_PREFER_SERVER_CIPHERS | off |
NGINX_SSL_SESSION_CACHE | shared:SSL:10m |
NGINX_SSL_SESSION_TIMEOUT | 1d |
Session tickets are disabled. TLS 1.0 and 1.1 are refused. ssl_session_cache and ssl_session_timeout are http-level directives, so the entrypoint patches them into the rendered nginx.conf rather than the server block.
Session cookies
When NGINX_SSL_ENABLED=true, PHP_SESSION_COOKIE_SECURE resolves to On automatically, so PHPSESSID carries the Secure flag. Override it explicitly if you terminate TLS elsewhere and still want the flag.
Health checks
The image's HEALTHCHECK runs healthcheck.sh, which probes /health on the redirect port over HTTP when the redirect is on, or on the HTTPS port (ignoring the certificate) when it is off. Point external probes at the same endpoint.
Read-only root filesystem
If you run with --read-only and NGINX_SSL_SELF_SIGNED=true, add --tmpfs /etc/nginx/ssl:uid=1000,gid=1000 so the certificate can be written. See Users and permissions.