#!/bin/bash DOMAIN=$1 TARGET_RAW=$2 PATTERNS=$3 REAL_IP_OVERRIDE=$4 HOSTS_LINE="127.0.0.1 $DOMAIN" # Parse TARGET: support both host:port and full URLs (http:// or https://) if [[ "$TARGET_RAW" =~ ^https?:// ]]; then TARGET_SCHEME=$(echo "$TARGET_RAW" | sed -E 's|^(https?)://.*|\1|') TARGET_HOST=$(echo "$TARGET_RAW" | sed -E 's|^https?://([^/:]+).*|\1|') TARGET_PORT=$(echo "$TARGET_RAW" | sed -E 's|^https?://[^/:]+:?([0-9]*)/?\s*$|\1|') if [ -z "$TARGET_PORT" ]; then if [ "$TARGET_SCHEME" = "https" ]; then TARGET_PORT=443 else TARGET_PORT=80 fi fi TARGET="${TARGET_HOST}:${TARGET_PORT}" else TARGET_SCHEME="http" TARGET="$TARGET_RAW" fi NGINX_CONF=$(mktemp) CERT_DIR=$(pwd) CLEANUP_DONE=0 # Resolve real IP before we mess with hosts if [ -n "$REAL_IP_OVERRIDE" ]; then REAL_IP="$REAL_IP_OVERRIDE" echo "Using provided IP: $REAL_IP" else # Use external DNS (8.8.8.8) to bypass /etc/hosts # Get final IP (not CNAME) by filtering for IP addresses only REAL_IP=$(dig +short "$DOMAIN" @8.8.8.8 | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -n1) if [ -z "$REAL_IP" ]; then echo "Could not resolve $DOMAIN" exit 1 fi # Check if resolved IP is localhost (proxy loop) if [[ "$REAL_IP" == "127.0.0.1" || "$REAL_IP" == "::1" || "$REAL_IP" == "localhost" ]]; then echo "ERROR: $DOMAIN resolves to localhost ($REAL_IP)" echo "This would cause a proxy loop." echo "Provide the real IP as the 4th argument:" echo " $0 $DOMAIN $TARGET \"$PATTERNS\" " exit 1 fi echo "Resolved $DOMAIN to $REAL_IP" fi cleanup() { [ "$CLEANUP_DONE" -eq 1 ] && return CLEANUP_DONE=1 echo "Cleaning up..." if [[ "$OSTYPE" == "darwin"* ]]; then sudo sed -i "" "\|$HOSTS_LINE|d" /etc/hosts else sudo sed -i "\|$HOSTS_LINE|d" /etc/hosts fi rm -f "$DOMAIN.pem" "$DOMAIN-key.pem" "$NGINX_CONF" echo "Done" } trap cleanup EXIT echo "$HOSTS_LINE" | sudo tee -a /etc/hosts mkcert "$DOMAIN" # Build passthrough location blocks PASSTHROUGH_LOCATIONS="" if [ -n "$PATTERNS" ]; then IFS=',' read -ra PATTERN_ARRAY <<< "$PATTERNS" for pattern in "${PATTERN_ARRAY[@]}"; do # Remove trailing * from pattern if present clean_pattern="${pattern%\*}" PASSTHROUGH_LOCATIONS+=" # Passthrough for /${clean_pattern} location ~* ^/${clean_pattern} { proxy_pass https://$REAL_IP; proxy_ssl_server_name on; proxy_ssl_name $DOMAIN; 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_http_version 1.1; proxy_set_header Connection \"\"; } " done fi if [ "$TARGET_SCHEME" = "https" ]; then # For HTTPS targets, use proxy_pass directly (no upstream block) # so nginx handles SNI correctly per-request LOCAL_DEV_BLOCK=" server { listen 443 ssl http2; server_name $DOMAIN; ssl_certificate $CERT_DIR/$DOMAIN.pem; ssl_certificate_key $CERT_DIR/$DOMAIN-key.pem; resolver 8.8.8.8 1.1.1.1 ipv6=off; # Passthrough patterns go to real upstream $PASSTHROUGH_LOCATIONS # Everything else goes to local dev server location / { set \$backend \"${TARGET_SCHEME}://${TARGET_HOST}:${TARGET_PORT}\"; proxy_pass \$backend; proxy_http_version 1.1; proxy_set_header Host ${TARGET_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_set_header X-Forwarded-Host \$host; proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection \"upgrade\"; proxy_read_timeout 86400; proxy_ssl_server_name on; proxy_ssl_name ${TARGET_HOST}; } }" else # For plain host:port targets, use upstream block as before LOCAL_DEV_BLOCK=" upstream local_dev { server $TARGET; } server { listen 443 ssl http2; server_name $DOMAIN; ssl_certificate $CERT_DIR/$DOMAIN.pem; ssl_certificate_key $CERT_DIR/$DOMAIN-key.pem; # Passthrough patterns go to real upstream $PASSTHROUGH_LOCATIONS # Everything else goes to local dev server location / { proxy_pass http://local_dev; proxy_http_version 1.1; 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_set_header X-Forwarded-Host \$host; proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection \"upgrade\"; proxy_read_timeout 86400; } }" fi cat > "$NGINX_CONF" <