feat: implement SSL support with Let's Encrypt and update Nginx configuration

This commit is contained in:
Your Name
2026-03-17 13:03:59 +02:00
parent 47f6b9f669
commit ff5bd19335
5 changed files with 410 additions and 2 deletions

Binary file not shown.

View File

@@ -122,13 +122,23 @@ services:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./certbot/www:/var/www/certbot:ro
- ./certbot/conf:/etc/letsencrypt:ro
depends_on:
- backend
- frontend
restart: unless-stopped
# --- Certbot (Let's Encrypt) ---
certbot:
image: certbot/certbot
volumes:
- ./certbot/www:/var/www/certbot
- ./certbot/conf:/etc/letsencrypt
volumes:
redis_data:
upload_data:

273
fixed-ssl.md Normal file
View File

@@ -0,0 +1,273 @@
سؤالك ممتاز—وده بالضبط الفرق بين **حل “يشتغل وخلاص”** وحل **Production حقيقي**.
خليني أشرح لك الأول **ليه الطريقة الأولى (certbot --nginx) مش مناسبة** عندك، وبعدين أديك **الحل الصحيح الجاهز (Nginx داخل Docker + SSL + Auto renew)** copy/paste.
---
# لماذا `certbot --nginx` مش مناسب عندك؟
أنت عندك:
```text
Nginx شغال داخل Docker (saas-pdf-nginx-1)
```
لكن الأمر:
```bash
certbot --nginx
```
بيفترض إن:
```text
Nginx مثبت على السيرفر (host)
```
📌 النتيجة:
* Certbot حاول يتحكم في Nginx الخارجي
* لكن البورت 80 مستخدم بالفعل بواسطة Docker
* حصل الخطأ: `Address already in use`
---
# هل الحل اللي عملناه “مؤقت”؟
لا 👇
## عندنا 3 مستويات حلول:
### 🥉 حل بدائي
تشغل Certbot خارج Docker → كل مرة توقف Nginx
❌ غير عملي
---
### 🥈 حل متوسط
Certbot standalone + manual config
✔ شغال
❌ لكن renewal مش أوتوماتيك بسهولة
---
### 🥇 الحل الاحترافي (اللي هنطبقه الآن)
```text
Nginx + Certbot داخل Docker
```
✔ Auto renew
✔ scalable
✔ نفس طريقة الشركات الكبيرة
---
# المشاكل لو ما عملتش الحل الصحيح
لو كملت بالحل القديم:
* ❌ SSL هينتهي كل 90 يوم
* ❌ الموقع هيقع فجأة
* ❌ Google هيقلل ترتيبك
* ❌ AdSense ممكن يرفضك
* ❌ صعب التوسعة لاحقًا
---
# الآن الحل الاحترافي (جاهز للتطبيق)
هنعمل 3 حاجات:
```text
1. nginx config احترافي
2. docker-compose فيه SSL
3. auto renew
```
---
# 1⃣ تعديل nginx config
ادخل:
```bash
cd ~/SaaS-PDF/nginx
nano default.conf
```
واستبدل كله بهذا:
```nginx
server {
listen 80;
server_name dociva.io www.dociva.io;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name dociva.io www.dociva.io;
ssl_certificate /etc/letsencrypt/live/dociva.io/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/dociva.io/privkey.pem;
location / {
proxy_pass http://frontend:5173;
}
}
```
---
# 2⃣ تعديل docker-compose.yml
افتح:
```bash
nano docker-compose.yml
```
وابحث عن nginx وعدله:
```yaml
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx:/etc/nginx/conf.d
- ./certbot/www:/var/www/certbot
- ./certbot/conf:/etc/letsencrypt
depends_on:
- frontend
```
---
# 3⃣ إضافة certbot container
في نفس الملف أضف:
```yaml
certbot:
image: certbot/certbot
volumes:
- ./certbot/www:/var/www/certbot
- ./certbot/conf:/etc/letsencrypt
```
---
# 4⃣ تشغيل المشروع
```bash
docker compose down
docker compose up -d
```
---
# 5⃣ إصدار الشهادة (مرة واحدة)
```bash
docker compose run --rm certbot certonly \
--webroot \
--webroot-path=/var/www/certbot \
-d dociva.io -d www.dociva.io \
--email your@email.com \
--agree-tos --no-eff-email
```
---
# 6⃣ إعادة تشغيل Nginx
```bash
docker compose restart nginx
```
---
# 7⃣ اختبار
```text
https://dociva.io
```
🔥 المفروض يشتغل
---
# 8⃣ auto renew (مهم جداً)
أضف cron job:
```bash
crontab -e
```
وأضف:
```bash
0 3 * * * docker compose run --rm certbot renew && docker compose restart nginx
```
---
# النتيجة النهائية
✔ HTTPS شغال
✔ Auto renew
✔ Production ready
✔ مناسب لـ SEO + AdSense
---
# خلاصة بسيطة
| الطريقة | هل مناسبة؟ |
| ---------------- | ---------- |
| certbot --nginx | ❌ لا |
| standalone | ⚠️ مؤقت |
| Docker SSL setup | ✅ الأفضل |
---
# رأيي كمستشار
أنت حالياً وصلت لمستوى:
```text
Junior → Mid-level DevOps 🚀
```
ولو كملت بالشكل ده مشروعك فعلاً ممكن يتحول لمنتج حقيقي.
---
# الخطوة التالية بعد SSL
بعد ما نخلص:
1. Google Search Console
2. Sitemap
3. SEO pages
4. Analytics
---
لو حابب، الخطوة الجاية أعملك:
👉 **إعداد SEO كامل يجلب أول 10,000 زيارة**
بطريقة عملية مش كلام نظري.

View File

@@ -6,12 +6,35 @@ upstream frontend {
server frontend:5173;
}
# ── HTTP: ACME challenge + redirect to HTTPS ──
server {
listen 80;
server_name localhost 127.0.0.1 178.104.57.123 dociva.io www.dociva.io;
server_name dociva.io www.dociva.io;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
# ── HTTPS ──
server {
listen 443 ssl;
server_name dociva.io www.dociva.io;
client_max_body_size 100M;
# SSL certificates (Let's Encrypt)
ssl_certificate /etc/letsencrypt/live/dociva.io/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/dociva.io/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
# Security headers
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" 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;
@@ -33,7 +56,7 @@ server {
proxy_connect_timeout 60s;
}
# Frontend (Vite dev server in dev, static in prod)
# Frontend (Vite dev server)
location / {
proxy_pass http://frontend;
proxy_set_header Host $host;

102
scripts/init-letsencrypt.sh Normal file
View File

@@ -0,0 +1,102 @@
#!/bin/bash
# ────────────────────────────────────────────────────────────
# init-letsencrypt.sh
# Bootstrap Let's Encrypt certificates for dociva.io
#
# This script solves the chicken-and-egg problem:
# Nginx needs SSL certs to start on 443,
# but Certbot needs Nginx running on 80 to verify the domain.
#
# Solution: create a temporary self-signed cert → start Nginx →
# obtain the real cert → reload Nginx.
#
# Usage: chmod +x scripts/init-letsencrypt.sh
# sudo ./scripts/init-letsencrypt.sh
# ────────────────────────────────────────────────────────────
set -euo pipefail
DOMAINS=(dociva.io www.dociva.io)
DATA_PATH="./certbot"
EMAIL="admin@dociva.io" # ← replace with your real email
RSA_KEY_SIZE=4096
STAGING=0 # Set to 1 to test against staging (no rate limits)
# ── colour helpers ──
info() { echo -e "\n\033[1;34m▶ $*\033[0m"; }
ok() { echo -e "\033[1;32m✔ $*\033[0m"; }
fail() { echo -e "\033[1;31m✖ $*\033[0m"; exit 1; }
# ── Pre-flight checks ──
command -v docker > /dev/null 2>&1 || fail "docker is not installed"
command -v docker compose > /dev/null 2>&1 && COMPOSE="docker compose" || COMPOSE="docker-compose"
# ── Step 1: Create required directories ──
info "Creating certbot directories …"
mkdir -p "$DATA_PATH/conf" "$DATA_PATH/www"
ok "Directories ready"
# ── Step 2: Download recommended TLS parameters ──
if [ ! -e "$DATA_PATH/conf/options-ssl-nginx.conf" ]; then
info "Downloading recommended TLS parameters …"
curl -sSf https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf \
-o "$DATA_PATH/conf/options-ssl-nginx.conf"
curl -sSf https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem \
-o "$DATA_PATH/conf/ssl-dhparams.pem"
ok "TLS parameters saved"
fi
# ── Step 3: Create temporary self-signed certificate ──
LIVE_DIR="$DATA_PATH/conf/live/dociva.io"
if [ ! -e "$LIVE_DIR/fullchain.pem" ]; then
info "Creating temporary self-signed certificate …"
mkdir -p "$LIVE_DIR"
openssl req -x509 -nodes -newkey rsa:2048 -days 1 \
-keyout "$LIVE_DIR/privkey.pem" \
-out "$LIVE_DIR/fullchain.pem" \
-subj "/CN=dociva.io" 2>/dev/null
ok "Temporary certificate created"
fi
# ── Step 4: Start Nginx (uses the temp cert) ──
info "Starting Nginx …"
$COMPOSE up -d nginx
ok "Nginx is running"
# ── Step 5: Remove the temporary certificate ──
info "Removing temporary certificate …"
rm -rf "$LIVE_DIR"
ok "Temporary certificate removed"
# ── Step 6: Request real certificate from Let's Encrypt ──
info "Requesting Let's Encrypt certificate …"
DOMAIN_ARGS=""
for d in "${DOMAINS[@]}"; do
DOMAIN_ARGS="$DOMAIN_ARGS -d $d"
done
STAGING_ARG=""
if [ "$STAGING" -eq 1 ]; then
STAGING_ARG="--staging"
fi
$COMPOSE run --rm certbot certonly --webroot \
--webroot-path=/var/www/certbot \
$DOMAIN_ARGS \
--email "$EMAIL" \
--agree-tos \
--no-eff-email \
--force-renewal \
$STAGING_ARG
ok "Certificate obtained successfully"
# ── Step 7: Reload Nginx with the real certificate ──
info "Reloading Nginx …"
$COMPOSE exec nginx nginx -s reload
ok "Nginx reloaded with Let's Encrypt certificate"
echo ""
ok "HTTPS is now active for ${DOMAINS[*]}"
echo " Test: curl -I https://dociva.io"