Overview
By default, ipMoodle runs on HTTP (port 80). For production deployments, you should enable HTTPS to secure communications between users and your Moodle server.
This guide covers two approaches:
Let’s Encrypt - Free, automated SSL certificates (recommended for public domains)
Custom Certificates - Self-signed or purchased certificates for internal/private deployments
Prerequisites
A registered domain name pointing to your server
Ports 80 and 443 open in your firewall
Access to modify docker-compose.yml and nginx configuration
Before enabling SSL, ensure your SITE_URL environment variable uses https:// instead of http://
Option 1: Let’s Encrypt with Certbot
Let’s Encrypt provides free SSL certificates with automatic renewal.
Initial Setup
Create certificates directory
mkdir -p ./nginx/ssl
mkdir -p ./certbot/conf
mkdir -p ./certbot/www
Update docker-compose.yml
Add the certbot service and update the web service volumes (source/docker-compose.yml:50-63): services :
# ... existing services ...
web :
image : nginx:alpine
container_name : moodle_web
restart : always
ports :
- "80:80"
- "443:443" # Add HTTPS port
volumes :
- ./html:/var/www/html:ro
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
- ./certbot/conf:/etc/letsencrypt:ro # Add certificate volume
- ./certbot/www:/var/www/certbot:ro # Add challenge volume
depends_on :
- app
networks :
- moodle-net
certbot :
image : certbot/certbot:latest
container_name : moodle_certbot
volumes :
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
entrypoint : "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
Update nginx configuration
Create nginx/default.conf with HTTP to HTTPS redirect and SSL configuration: # HTTP server - redirect to HTTPS
server {
listen 80 ;
server_name your-domain.com;
# Let's Encrypt challenge location
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# Redirect all other traffic to HTTPS
location / {
return 301 https://$ host $ request_uri ;
}
}
# HTTPS server
server {
listen 443 ssl http2;
server_name your-domain.com;
root /var/www/html/public;
index index.php;
# SSL certificates
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
# SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384' ;
ssl_prefer_server_ciphers off ;
# SSL session cache
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m ;
# Optimización de buffers y tiempos
client_max_body_size 512M ;
fastcgi_read_timeout 300 ;
# Slash Arguments (VITAL para Moodle)
location / {
try_files $ uri $ uri / /index.php$ is_args $ args ;
}
# Procesamiento PHP pasando al contenedor "app"
location ~ [^/]\.php(/|$) {
fastcgi_split_path_info ^(.+\.php)(/.+)$ ;
fastcgi_pass app:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $ document_root $ fastcgi_script_name ;
fastcgi_param PATH_INFO $ fastcgi_path_info ;
fastcgi_param HTTPS on ; # Tell PHP we're using HTTPS
}
# Seguridad: Bloquear acceso a archivos sensibles
location ~ (/vendor/|/node_modules/|composer\.json|/readme|/README|/LICENSE|/\.git) {
deny all ;
return 404 ;
}
}
Obtain initial certificate
Replace your-domain.com and [email protected] with your actual values: docker-compose up -d web
docker run --rm \
-v ./certbot/conf:/etc/letsencrypt \
-v ./certbot/www:/var/www/certbot \
certbot/certbot certonly \
--webroot \
--webroot-path=/var/www/certbot \
--email [email protected] \
--agree-tos \
--no-eff-email \
-d your-domain.com \
-d www.your-domain.com
Start all services
docker-compose down
docker-compose up -d
Automatic Certificate Renewal
The certbot container automatically renews certificates every 12 hours. To manually renew:
docker-compose exec certbot certbot renew
Let’s Encrypt certificates are valid for 90 days. The certbot container will automatically renew them when they have 30 days or less remaining.
Option 2: Custom SSL Certificates
For internal deployments or when using purchased certificates:
Prepare certificate files
Place your certificate files in ./nginx/ssl/:
certificate.crt - Your SSL certificate
private.key - Your private key
ca_bundle.crt - Certificate authority bundle (optional)
Update docker-compose.yml
Add the SSL directory to web service volumes: web :
image : nginx:alpine
container_name : moodle_web
restart : always
ports :
- "80:80"
- "443:443"
volumes :
- ./html:/var/www/html:ro
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro # Add SSL directory
depends_on :
- app
networks :
- moodle-net
Configure nginx for custom certificates
Update nginx/default.conf: server {
listen 80 ;
server_name your-domain.com;
return 301 https://$ host $ request_uri ;
}
server {
listen 443 ssl http2;
server_name your-domain.com;
root /var/www/html/public;
index index.php;
# Custom SSL certificates
ssl_certificate /etc/nginx/ssl/certificate.crt;
ssl_certificate_key /etc/nginx/ssl/private.key;
# Include CA bundle if available
# ssl_trusted_certificate /etc/nginx/ssl/ca_bundle.crt;
# SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384' ;
ssl_prefer_server_ciphers off ;
# ... rest of the configuration from the current default.conf ...
}
Restart services
docker-compose restart web
Generating Self-Signed Certificates
For testing or internal use:
mkdir -p ./nginx/ssl
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout ./nginx/ssl/private.key \
-out ./nginx/ssl/certificate.crt \
-subj "/C=US/ST=State/L=City/O=Organization/CN=your-domain.com"
Self-signed certificates will show browser warnings. Only use them for development or internal deployments.
Enhanced SSL Security Configuration
For stronger security, add these directives to your nginx HTTPS server block:
# HSTS (HTTP Strict Transport Security)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# OCSP Stapling
ssl_stapling on ;
ssl_stapling_verify on ;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s ;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
Update Moodle Configuration
After enabling SSL, update your Moodle configuration:
Update SITE_URL environment variable
Edit your .env file: SITE_URL = https://your-domain.com
Update config.php
Edit ./html/config.php: $CFG -> wwwroot = 'https://your-domain.com' ;
$CFG -> sslproxy = false ; // Set to true if behind SSL-terminating proxy
Restart application
docker-compose restart app
Testing SSL Configuration
Verify your SSL setup:
# Test certificate validity
openssl s_client -connect your-domain.com:443 -servername your-domain.com
# Check certificate expiration
echo | openssl s_client -connect your-domain.com:443 2> /dev/null | openssl x509 -noout -dates
# Test SSL rating (external tool)
curl -s https://www.ssllabs.com/ssltest/analyze.html?d=your-domain.com
Check nginx configuration syntax
docker-compose exec web nginx -t
You should see: nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Troubleshooting
Certificate errors
Certificate file not found
Ensure certificate paths in nginx configuration match the mounted volume paths: ls -la ./nginx/ssl/
ls -la ./certbot/conf/live/your-domain.com/
Let's Encrypt rate limits
Let’s Encrypt has rate limits (50 certificates per domain per week). Use staging environment for testing: Remove --staging flag for production certificates.
Mixed content warnings
HTTP resources on HTTPS page
Moodle may serve some resources over HTTP. Enable HTTPS enforcement in Moodle: Site administration > Security > HTTP security
Enable “Use HTTPS for logins”
Set “Cookie secure” to Yes
Redirect loops
Check that Moodle’s $CFG->wwwroot matches your actual domain and protocol: // In ./html/config.php
$CFG -> wwwroot = 'https://your-domain.com' ; // Must match nginx server_name
After changing SSL configuration, clear your browser cache and Moodle caches from Site administration > Development > Purge all caches