Skip to content

Instantly share code, notes, and snippets.

@PrashantBhatasana
Created November 20, 2025 05:02
Show Gist options
  • Select an option

  • Save PrashantBhatasana/d16cd49b73100d9fde929bc267bc1b3c to your computer and use it in GitHub Desktop.

Select an option

Save PrashantBhatasana/d16cd49b73100d9fde929bc267bc1b3c to your computer and use it in GitHub Desktop.
Complete Nginx + Certbot SSL Setup Script
#!/bin/bash
# ================================================
# Multi-Domain Nginx SSL Management
# ================================================
# Manage multiple domains with different configurations
# ================================================
set -e
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# Configuration file
CONFIG_FILE="/etc/nginx/domains.conf"
NGINX_DIR="/etc/nginx"
# Function to add a new domain
add_domain() {
local DOMAIN=$1
local EMAIL=$2
local PROXY_PORT=${3:-3000}
local DOMAIN_TYPE=${4:-"proxy"} # proxy, static, redirect
echo -e "${BLUE}Adding domain: ${DOMAIN}${NC}"
# Create configuration based on type
case $DOMAIN_TYPE in
proxy)
create_proxy_config $DOMAIN $EMAIL $PROXY_PORT
;;
static)
create_static_config $DOMAIN $EMAIL
;;
redirect)
local REDIRECT_TO=$5
create_redirect_config $DOMAIN $EMAIL $REDIRECT_TO
;;
*)
echo -e "${RED}Unknown domain type: ${DOMAIN_TYPE}${NC}"
exit 1
;;
esac
}
# Function to create proxy configuration
create_proxy_config() {
local DOMAIN=$1
local EMAIL=$2
local PORT=$3
cat > ${NGINX_DIR}/sites-available/${DOMAIN} << EOF
# Auto-generated Nginx configuration for ${DOMAIN}
# Type: Reverse Proxy
# Backend Port: ${PORT}
# Generated: $(date)
server {
listen 80;
server_name ${DOMAIN} www.${DOMAIN};
return 301 https://\$server_name\$request_uri;
}
server {
listen 443 ssl http2;
server_name ${DOMAIN} www.${DOMAIN};
# SSL Configuration (managed by Certbot)
ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem;
# SSL Security Settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
# Security Headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Logging
access_log /var/log/nginx/${DOMAIN}_access.log;
error_log /var/log/nginx/${DOMAIN}_error.log;
# Proxy Configuration
location / {
proxy_pass http://localhost:${PORT};
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection 'upgrade';
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;
proxy_cache_bypass \$http_upgrade;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# Buffering
proxy_buffering off;
proxy_request_buffering off;
}
# Health check endpoint
location /health {
access_log off;
return 200 "healthy\\n";
add_header Content-Type text/plain;
}
}
EOF
# Enable site
ln -sf ${NGINX_DIR}/sites-available/${DOMAIN} ${NGINX_DIR}/sites-enabled/
# Obtain SSL certificate
certbot certonly --nginx --non-interactive --agree-tos --email ${EMAIL} -d ${DOMAIN} -d www.${DOMAIN}
# Reload Nginx
nginx -t && systemctl reload nginx
echo -e "${GREEN}✅ Proxy configuration created for ${DOMAIN} -> localhost:${PORT}${NC}"
}
# Function to create static site configuration
create_static_config() {
local DOMAIN=$1
local EMAIL=$2
local WEB_ROOT="/var/www/${DOMAIN}"
# Create web root
mkdir -p ${WEB_ROOT}
cat > ${NGINX_DIR}/sites-available/${DOMAIN} << EOF
# Auto-generated Nginx configuration for ${DOMAIN}
# Type: Static Website
# Web Root: ${WEB_ROOT}
# Generated: $(date)
server {
listen 80;
server_name ${DOMAIN} www.${DOMAIN};
return 301 https://\$server_name\$request_uri;
}
server {
listen 443 ssl http2;
server_name ${DOMAIN} www.${DOMAIN};
# SSL Configuration (managed by Certbot)
ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem;
# SSL Security Settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Security Headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
# Web Root
root ${WEB_ROOT};
index index.html index.htm;
# Logging
access_log /var/log/nginx/${DOMAIN}_access.log;
error_log /var/log/nginx/${DOMAIN}_error.log;
location / {
try_files \$uri \$uri/ =404;
}
# Cache static assets
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
}
EOF
# Create default index.html
cat > ${WEB_ROOT}/index.html << EOF
<!DOCTYPE html>
<html>
<head>
<title>${DOMAIN}</title>
</head>
<body>
<h1>Welcome to ${DOMAIN}</h1>
<p>Your static website is ready!</p>
</body>
</html>
EOF
# Set permissions
chown -R www-data:www-data ${WEB_ROOT}
# Enable site
ln -sf ${NGINX_DIR}/sites-available/${DOMAIN} ${NGINX_DIR}/sites-enabled/
# Obtain SSL certificate
certbot certonly --nginx --non-interactive --agree-tos --email ${EMAIL} -d ${DOMAIN} -d www.${DOMAIN}
# Reload Nginx
nginx -t && systemctl reload nginx
echo -e "${GREEN}✅ Static site configuration created for ${DOMAIN}${NC}"
}
# Function to create redirect configuration
create_redirect_config() {
local DOMAIN=$1
local EMAIL=$2
local REDIRECT_TO=$3
cat > ${NGINX_DIR}/sites-available/${DOMAIN} << EOF
# Auto-generated Nginx configuration for ${DOMAIN}
# Type: Redirect
# Redirect To: ${REDIRECT_TO}
# Generated: $(date)
server {
listen 80;
server_name ${DOMAIN} www.${DOMAIN};
return 301 https://${REDIRECT_TO}\$request_uri;
}
server {
listen 443 ssl http2;
server_name ${DOMAIN} www.${DOMAIN};
# SSL Configuration (managed by Certbot)
ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem;
# Permanent redirect
return 301 https://${REDIRECT_TO}\$request_uri;
}
EOF
# Enable site
ln -sf ${NGINX_DIR}/sites-available/${DOMAIN} ${NGINX_DIR}/sites-enabled/
# Obtain SSL certificate
certbot certonly --nginx --non-interactive --agree-tos --email ${EMAIL} -d ${DOMAIN} -d www.${DOMAIN}
# Reload Nginx
nginx -t && systemctl reload nginx
echo -e "${GREEN}✅ Redirect configuration created: ${DOMAIN} -> ${REDIRECT_TO}${NC}"
}
# Function to remove a domain
remove_domain() {
local DOMAIN=$1
echo -e "${YELLOW}Removing domain: ${DOMAIN}${NC}"
# Remove Nginx configuration
rm -f ${NGINX_DIR}/sites-enabled/${DOMAIN}
rm -f ${NGINX_DIR}/sites-available/${DOMAIN}
# Remove web root if exists
rm -rf /var/www/${DOMAIN}
# Revoke certificate (optional)
read -p "Revoke SSL certificate? (y/n): " REVOKE
if [ "$REVOKE" == "y" ]; then
certbot revoke --cert-path /etc/letsencrypt/live/${DOMAIN}/cert.pem
certbot delete --cert-name ${DOMAIN}
fi
# Reload Nginx
nginx -t && systemctl reload nginx
echo -e "${GREEN}✅ Domain ${DOMAIN} removed${NC}"
}
# Function to list all domains
list_domains() {
echo -e "${BLUE}Configured Domains:${NC}"
echo ""
for config in ${NGINX_DIR}/sites-enabled/*; do
if [ -f "$config" ]; then
DOMAIN=$(basename $config)
echo -e "${GREEN}• ${DOMAIN}${NC}"
# Check SSL certificate
if [ -d "/etc/letsencrypt/live/${DOMAIN}" ]; then
EXPIRY=$(openssl x509 -enddate -noout -in "/etc/letsencrypt/live/${DOMAIN}/cert.pem" | cut -d= -f2)
echo " SSL Expiry: ${EXPIRY}"
fi
# Check if site is responding
if curl -sSf -o /dev/null "https://${DOMAIN}" 2>/dev/null; then
echo " Status: ✅ Online"
else
echo " Status: ❌ Offline"
fi
echo ""
fi
done
}
# Function to renew all SSL certificates
renew_certificates() {
echo -e "${BLUE}Renewing SSL certificates...${NC}"
certbot renew
systemctl reload nginx
echo -e "${GREEN}✅ Certificate renewal complete${NC}"
}
# Function to backup configurations
backup_nginx() {
local BACKUP_DIR="/backup/nginx/$(date +%Y%m%d_%H%M%S)"
echo -e "${BLUE}Backing up Nginx configurations...${NC}"
mkdir -p ${BACKUP_DIR}
# Backup Nginx configs
cp -r ${NGINX_DIR}/sites-available ${BACKUP_DIR}/
cp -r ${NGINX_DIR}/sites-enabled ${BACKUP_DIR}/
# Backup SSL certificates
cp -r /etc/letsencrypt ${BACKUP_DIR}/
# Create tar archive
tar -czf ${BACKUP_DIR}.tar.gz -C ${BACKUP_DIR} .
rm -rf ${BACKUP_DIR}
echo -e "${GREEN}✅ Backup saved to ${BACKUP_DIR}.tar.gz${NC}"
}
# Main menu
show_menu() {
echo -e "${BLUE}================================================${NC}"
echo -e "${BLUE}Multi-Domain Nginx Management${NC}"
echo -e "${BLUE}================================================${NC}"
echo ""
echo "1. Add proxy domain (app backend)"
echo "2. Add static website"
echo "3. Add redirect domain"
echo "4. Remove domain"
echo "5. List all domains"
echo "6. Renew SSL certificates"
echo "7. Backup configurations"
echo "8. Exit"
echo ""
read -p "Select option: " OPTION
case $OPTION in
1)
read -p "Enter domain: " DOMAIN
read -p "Enter email: " EMAIL
read -p "Enter backend port (default 3000): " PORT
PORT=${PORT:-3000}
add_domain $DOMAIN $EMAIL $PORT "proxy"
;;
2)
read -p "Enter domain: " DOMAIN
read -p "Enter email: " EMAIL
add_domain $DOMAIN $EMAIL 0 "static"
;;
3)
read -p "Enter domain: " DOMAIN
read -p "Enter email: " EMAIL
read -p "Redirect to domain: " REDIRECT_TO
add_domain $DOMAIN $EMAIL 0 "redirect" $REDIRECT_TO
;;
4)
read -p "Enter domain to remove: " DOMAIN
remove_domain $DOMAIN
;;
5)
list_domains
;;
6)
renew_certificates
;;
7)
backup_nginx
;;
8)
exit 0
;;
*)
echo -e "${RED}Invalid option${NC}"
;;
esac
}
# Command line interface
if [ "$1" == "add-proxy" ]; then
add_domain $2 $3 ${4:-3000} "proxy"
elif [ "$1" == "add-static" ]; then
add_domain $2 $3 0 "static"
elif [ "$1" == "add-redirect" ]; then
add_domain $2 $3 0 "redirect" $4
elif [ "$1" == "remove" ]; then
remove_domain $2
elif [ "$1" == "list" ]; then
list_domains
elif [ "$1" == "renew" ]; then
renew_certificates
elif [ "$1" == "backup" ]; then
backup_nginx
else
show_menu
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment