ساخت CDN شخصی و سرویس انتقال فایل با Nginx؛ فایل موقت، Static CDN و Image CDN
اگر فایل، تصویر، CSS، JavaScript یا فایلهای حجیم را مستقیماً از همان سرور اصلی سایت تحویل دهید، با افزایش کاربران هم مصرف پهنای باند Origin بیشتر میشود و هم کاربران دورتر تأخیر بیشتری احساس میکنند. یک معماری بهتر این است که Origin، سرویس انتقال فایل، Cache Node و Image CDN را از هم جدا کنیم.
در این آموزش یک پلتفرم شخصی برای تحویل فایل میسازیم که سه کاربرد را همزمان پوشش میدهد: انتقال فایل موقت با تاریخ انقضا، CDN شخصی برای فایلهای استاتیک و Image CDN برای Resize و تبدیل تصاویر به WebP یا AVIF. هسته اصلی معماری Nginx است و برای بخش فایل موقت از Python و FastAPI و برای پردازش تصویر از imgproxy استفاده میکنیم.
📌 در پایان این پروژه خواهیم داشت:
🔹 سرویس انتقال فایل با لینک موقت و تاریخ انقضا
🔹 Origin Server برای فایلهای اصلی
🔹 Edge Cache با Nginx
🔹 کش خودکار CSS، JS، فونت و فایلهای استاتیک
🔹 Image CDN با Resize و Crop لحظهای
🔹 خروجی WebP و AVIF
🔹 URL امضاشده برای جلوگیری از سوءاستفاده از Image Processor
🔹 امکان ساخت چند Edge در ایران و خارج
اول یک نکته مهم؛ یک VPS بهتنهایی CDN نیست
اگر فقط یک VPS داشته باشید و فایلها را از روی آن تحویل دهید، در واقع یک File Server یا Cache Server دارید؛ نه یک CDN واقعی.
CDN زمانی معنا پیدا میکند که فایل اصلی روی Origin قرار داشته باشد و چند Edge Node در موقعیتهای شبکهای یا جغرافیایی مختلف آن را Cache کنند. کاربر نیز تا حد امکان از Edge نزدیکتر پاسخ میگیرد.
مثلاً معماری میتواند چنین باشد:
+----------------+
| Origin Server |
| Files/Images |
+--------+-------+
|
+---------------+---------------+
| |
v v
+---------------+ +---------------+
| Edge - Iran | | Edge - Europe |
| Nginx Cache | | Nginx Cache |
+-------+-------+ +-------+-------+
| |
v v
Iranian Users Other Users
برای توزیع خودکار کاربران میان Edgeها به GeoDNS، Anycast یا مکانیزم Routing دیگری نیاز دارید. در پروژههای کوچکتر میتوانید برای هر منطقه Subdomain جدا تعریف کنید یا با DNS Round Robin شروع کنید؛ هرچند Round Robin الزاماً نزدیکترین سرور را انتخاب نمیکند.
معماری نهایی پروژه
برای اینکه اجزای پروژه قاطی نشوند، چهار Hostname در نظر میگیریم:
| Hostname | کاربرد |
|---|---|
| transfer.example.com | آپلود و انتقال فایل موقت |
| origin.example.com | Origin فایلهای استاتیک و تصاویر اصلی |
| cdn.example.com | Static CDN و Edge Cache |
| img.example.com | Image CDN و پردازش تصویر |
برای این پروژه چه سروری نیاز داریم؟
برای شروع میتوانید Origin، FastAPI و imgproxy را روی یک VPS لینوکس قرار دهید و بعد Edgeها را روی VPSهای جدا اضافه کنید.
| نقش | CPU پیشنهادی شروع | RAM | نکته مهم |
|---|---|---|---|
| Origin + Transfer | 2 vCPU | 2GB+ | Storage و Traffic |
| Edge Cache | 1 تا 2 vCPU | 1 تا 2GB | پورت شبکه و Disk Cache |
| Image CDN | 2 تا 4 vCPU | 2 تا 4GB | CPU برای Resize و Encoding |
ساخت CDN شخصی با چند VPS ایرانیکاسرور
برای شروع میتوانید Origin را روی یک سرور مجازی ایران قرار دهید و در صورت نیاز Edgeهای دیگر را روی VPSهای جداگانه بسازید. اگر کاربران شما هم داخل و هم خارج ایران هستند، جدا کردن Edge ایران و خارج میتواند Architecture مناسبی برای آزمایش CDN شخصی باشد.
مرحله اول: آمادهسازی Origin روی Ubuntu 24.04
sudo apt update sudo apt upgrade -y sudo apt install nginx python3 python3-venv python3-pip sqlite3 curl ca-certificates -y
مسیرهای اصلی پروژه را ایجاد کنید:
sudo mkdir -p /srv/file-platform/static sudo mkdir -p /srv/file-platform/images sudo mkdir -p /srv/file-platform/transfers sudo mkdir -p /opt/file-platform
بخش اول: ساخت سرویس انتقال فایل موقت
سرویس انتقال فایل ما شبیه یک نسخه شخصی و ساده از سرویسهای Temporary File Sharing عمل میکند. فایل Upload میشود، Token تصادفی ساخته میشود و لینک تا زمان مشخصی معتبر میماند.
در این بخش لینک یکبارمصرف نیست؛ تا پایان زمان اعتبار میتواند چند بار Download شود. برای لینک کاملاً یکبارمصرف باید وضعیت مصرف Token نیز ثبت شود.
ساخت Virtual Environment
cd /opt/file-platform python3 -m venv venv ./venv/bin/pip install fastapi "uvicorn[standard]" python-multipart
کد FastAPI
فایل زیر را بسازید:
nano /opt/file-platform/app.py
import hashlib
import os
import secrets
import sqlite3
import time
import uuid
from pathlib import Path
from urllib.parse import quote
from fastapi import (
FastAPI,
File,
Form,
Header,
HTTPException,
UploadFile,
)
from fastapi.responses import Response
app = FastAPI()
BASE_URL = os.getenv(
"BASE_URL",
"https://transfer.example.com"
).rstrip("/")
ADMIN_KEY = os.getenv(
"ADMIN_KEY",
"CHANGE-ME"
)
FILE_ROOT = Path(
"/srv/file-platform/transfers"
).resolve()
DB_PATH = Path(
"/opt/file-platform/transfers.db"
)
MAX_BYTES = 5 * 1024 * 1024 * 1024
def db():
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
return conn
def init_db():
conn = db()
conn.execute("""
CREATE TABLE IF NOT EXISTS transfers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
token_hash TEXT UNIQUE NOT NULL,
stored_name TEXT NOT NULL,
original_name TEXT NOT NULL,
expires_at INTEGER NOT NULL,
created_at INTEGER NOT NULL
)
""")
conn.commit()
conn.close()
def token_hash(token):
return hashlib.sha256(
token.encode()
).hexdigest()
init_db()
@app.post("/api/upload")
async def upload_file(
file: UploadFile = File(...),
ttl_minutes: int = Form(60),
x_admin_key: str | None = Header(None),
):
if not secrets.compare_digest(
x_admin_key or "",
ADMIN_KEY
):
raise HTTPException(
status_code=401,
detail="Unauthorized"
)
if ttl_minutes < 1 or ttl_minutes > 10080:
raise HTTPException(
status_code=400,
detail="Invalid TTL"
)
original = Path(
file.filename or "file.bin"
).name
suffix = Path(original).suffix[:16]
stored = (
uuid.uuid4().hex
+ suffix
)
temp_path = FILE_ROOT / (
stored + ".part"
)
final_path = FILE_ROOT / stored
total = 0
try:
with temp_path.open("wb") as output:
while True:
chunk = await file.read(
1024 * 1024
)
if not chunk:
break
total += len(chunk)
if total > MAX_BYTES:
raise HTTPException(
status_code=413,
detail="File too large"
)
output.write(chunk)
temp_path.rename(final_path)
except Exception:
temp_path.unlink(
missing_ok=True
)
raise
token = secrets.token_urlsafe(32)
digest = token_hash(token)
now = int(time.time())
expires = (
now
+ ttl_minutes * 60
)
conn = db()
conn.execute("""
INSERT INTO transfers (
token_hash,
stored_name,
original_name,
expires_at,
created_at
)
VALUES (?, ?, ?, ?, ?)
""", (
digest,
stored,
original,
expires,
now
))
conn.commit()
conn.close()
return {
"url": f"{BASE_URL}/t/{token}",
"expires_at": expires,
"size": total,
}
@app.get("/t/{token}")
def download(token: str):
digest = token_hash(token)
now = int(time.time())
conn = db()
row = conn.execute("""
SELECT
stored_name,
original_name,
expires_at
FROM transfers
WHERE token_hash = ?
""", (digest,)).fetchone()
conn.close()
if row is None:
raise HTTPException(
status_code=404
)
if row["expires_at"] <= now:
raise HTTPException(
status_code=410,
detail="Link expired"
)
response = Response(
status_code=200
)
response.headers[
"X-Accel-Redirect"
] = (
"/_transfer/"
+ quote(row["stored_name"])
)
response.headers[
"Content-Disposition"
] = (
"attachment; "
"filename*=UTF-8''"
+ quote(row["original_name"])
)
response.headers[
"Cache-Control"
] = "private, no-store"
response.headers[
"X-Robots-Tag"
] = "noindex, nofollow, noarchive"
return response
در این کد Token خام داخل دیتابیس ذخیره نمیشود و فقط SHA-256 آن نگهداری میشود. فایل نیز با نام تصادفی روی Disk ذخیره میشود تا نام اصلی فایل مسیر واقعی Storage را تعیین نکند.
اجرای FastAPI با systemd
یک کلید مدیریتی تصادفی بسازید:
openssl rand -hex 32
سپس Unit بسازید:
sudo nano \ /etc/systemd/system/file-transfer.service
[Unit]
Description=Temporary File Transfer API
After=network.target
[Service]
WorkingDirectory=/opt/file-platform
Environment="BASE_URL=https://transfer.example.com"
Environment="ADMIN_KEY=YOUR_RANDOM_ADMIN_KEY"
ExecStart=/opt/file-platform/venv/bin/uvicorn
app:app
--host 127.0.0.1
--port 9000
--workers 2
Restart=on-failure
RestartSec=3
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload sudo systemctl enable --now file-transfer sudo systemctl status file-transfer
تنظیم Nginx برای سرویس انتقال فایل
server {
listen 80;
server_name transfer.example.com;
client_max_body_size 5g;
location /api/ {
proxy_request_buffering off;
proxy_pass http://127.0.0.1:9000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /t/ {
proxy_pass http://127.0.0.1:9000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /_transfer/ {
internal;
alias /srv/file-platform/transfers/;
add_header X-Content-Type-Options nosniff always;
}
}
Location با نام /_transfer/ عمداً internal است. کاربر نمیتواند مسیر فایل واقعی را مستقیم باز کند؛ فقط FastAPI بعد از تأیید Token با هدر X-Accel-Redirect اجازه تحویل فایل را صادر میکند.
تست سرویس انتقال فایل
مثلاً برای آپلود فایل با اعتبار 120 دقیقه:
curl -X POST \ -H "X-Admin-Key: YOUR_RANDOM_ADMIN_KEY" \ -F "ttl_minutes=120" \ -F "file=@backup.zip" \ https://transfer.example.com/api/upload
پاسخ نمونه:
{
"url": "https://transfer.example.com/t/....",
"expires_at": 1790960000,
"size": 52428800
}
پاک کردن خودکار فایلهای منقضیشده
اگر فایلهای موقت پاک نشوند، Disk بهمرور پر میشود. بهتر است یک Cleanup Job دورهای براساس expires_at داشته باشید.
منطق Cleanup باید رکوردهای منقضیشده را از SQLite بخواند، فایل stored_name مربوط به هر رکورد را حذف کند و سپس رکورد دیتابیس را پاک کند. برای محیط کوچک اجرای این Job هر 15 یا 30 دقیقه کافی است.
بخش دوم: ساخت CDN شخصی برای فایلهای استاتیک
حالا سراغ فایلهایی میرویم که موقت نیستند؛ مانند CSS، JavaScript، فونت، ZIP، PDF یا Assetهای سایت.
فایل اصلی فقط روی Origin قرار میگیرد و Edgeها اولین درخواست را از Origin دریافت میکنند. درخواستهای بعدی تا پایان TTL مستقیماً از Cache همان Edge پاسخ داده میشوند.
کانفیگ Origin
server {
listen 80;
server_name origin.example.com;
location /static/ {
alias /srv/file-platform/static/;
autoindex off;
expires 7d;
add_header Cache-Control \
"public, max-age=604800, immutable";
}
location /images/ {
alias /srv/file-platform/images/;
autoindex off;
expires 30d;
add_header Cache-Control \
"public, max-age=2592000, immutable";
}
}
برای Cache طولانی حتماً فایلها را Version کنید
اگر فایل app.css را با Cache یکهفتهای منتشر کنید و بعد محتوای همان URL را تغییر دهید، برخی کاربران تا پایان TTL نسخه قبلی را خواهند دید.
راه بهتر استفاده از نام نسخهدار یا Hash است:
app.4f87c2.css app.v17.js logo.20261002.svg
در این مدل میتوانید Cache-Control طولانیتری بدهید؛ چون هر تغییر فایل یک URL جدید ایجاد میکند.
ساخت اولین Edge Node با Nginx Proxy Cache
روی VPS دوم Nginx نصب کنید:
sudo apt update sudo apt install nginx -y
در بخش http فایل nginx.conf یک Cache Zone ایجاد کنید:
proxy_cache_path \ /var/cache/nginx/static \ levels=1:2 \ keys_zone=STATIC_CACHE:50m \ max_size=20g \ inactive=7d \ use_temp_path=off;
حالا Virtual Host مربوط به Edge:
server {
listen 80;
server_name cdn.example.com;
location / {
proxy_cache STATIC_CACHE;
proxy_cache_key \
"$scheme$proxy_host$request_uri";
proxy_cache_lock on;
proxy_cache_valid 200 301 302 7d;
proxy_cache_valid 404 1m;
proxy_cache_use_stale \
error \
timeout \
updating \
http_500 \
http_502 \
http_503 \
http_504;
proxy_pass \
https://origin.example.com;
proxy_set_header \
Host origin.example.com;
proxy_ssl_server_name on;
add_header \
X-Cache-Status \
$upstream_cache_status \
always;
}
}
چطور بفهمیم CDN واقعاً Cache میکند؟
یک فایل را دو بار درخواست کنید:
curl -I \ https://cdn.example.com/static/app.v1.js
اولین درخواست معمولاً:
X-Cache-Status: MISS
درخواست بعدی باید در شرایط عادی چیزی شبیه این باشد:
X-Cache-Status: HIT
چرا proxy_cache_lock مهم است؟
فرض کنید یک فایل جدید منتشر شده و در یک لحظه صدها کاربر آن را درخواست کنند. اگر Cache خالی باشد، بدون Lock ممکن است تعداد زیادی درخواست همزمان به Origin برسد.
با proxy_cache_lock فقط یک Request Cache را پر میکند و Requestهای دیگر منتظر نتیجه میمانند؛ این رفتار فشار ناگهانی روی Origin را کمتر میکند.
اگر Origin قطع شود چه میشود؟
دستور proxy_cache_use_stale اجازه میدهد در برخی خطاهای Origin نسخه Cacheشده قبلی همچنان به کاربر تحویل داده شود.
این یکی از مزیتهای مهم Edge Cache است؛ قطعی کوتاه Origin الزاماً به معنی قطع فوری تمام Assetهای Cacheشده نیست.
Cache فایلهای بسیار بزرگ
اگر قرار است فایلهای چندگیگابایتی را Cache کنید، Byte Range اهمیت پیدا میکند. Nginx ماژولی به نام slice دارد که Response بزرگ را به بخشهای کوچکتر تقسیم میکند تا Cache کردن Rangeها مؤثرتر شود.
اما این Module در همه Buildهای Nginx بهصورت پیشفرض فعال نیست؛ قبل از استفاده باید Build فعلی را بررسی کنید.
چطور CDN را چندنقطهای کنیم؟
بعد از اینکه اولین Edge کار کرد، میتوانید همان Configuration را روی VPSهای دیگری Deploy کنید.
برای نمونه:
ir-edge.example.com eu-edge.example.com cdn.example.com
اگر Routing هوشمند ندارید، میتوانید ابتدا Application را طوری تنظیم کنید که براساس موقعیت یا نوع کاربر Hostname مناسب را انتخاب کند. راه حرفهایتر استفاده از GeoDNS یا Anycast است.
بخش سوم: ساخت Image CDN شخصی
Static CDN فقط همان فایل اصلی را Cache میکند، اما Image CDN یک کار اضافه انجام میدهد: تصویر اصلی را در لحظه Resize، Crop یا Convert میکند و خروجی تولیدشده را Cache میکند.
مثلاً فقط یک تصویر 4000×3000 روی Origin دارید، اما سایت به چند نسخه نیاز دارد:
120x120 avatar 400x300 card 800x600 article 1600x900 hero
بهجای ساخت دستی همه نسخهها، imgproxy آنها را On-Demand تولید میکند.
imgproxy چیست؟
imgproxy یک سرویس مستقل برای Resize، Crop، تبدیل و Optimize تصویر است که روی libvips کار میکند. نسخه متنباز آن از فرمتهایی مانند JPEG، PNG، WebP، AVIF و GIF پشتیبانی میکند.
مزیت آن این است که Application اصلی شما دیگر درگیر پردازش تصویر نمیشود.
نصب Docker روی Ubuntu
Docker را از Repository رسمی نصب کنید:
sudo apt update
sudo apt install ca-certificates curl -y
sudo install -m 0755
-d /etc/apt/keyrings
sudo curl -fsSL
https://download.docker.com/linux/ubuntu/gpg
-o /etc/apt/keyrings/docker.asc
sudo chmod a+r
/etc/apt/keyrings/docker.asc
sudo tee
/etc/apt/sources.list.d/docker.sources
>/dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
sudo apt update
sudo apt install
docker-ce
docker-ce-cli
containerd.io
docker-buildx-plugin
docker-compose-plugin
-y
اجرای imgproxy
کلید و Salt تولید کنید:
openssl rand -hex 32 openssl rand -hex 32
یک پوشه بسازید:
sudo mkdir -p /opt/imgproxy cd /opt/imgproxy
فایل .env:
IMGPROXY_KEY=YOUR_HEX_KEY IMGPROXY_SALT=YOUR_HEX_SALT IMGPROXY_SIGNATURE_SIZE=32 IMGPROXY_AUTO_WEBP=true IMGPROXY_AUTO_AVIF=true
Docker Compose:
services:
imgproxy:
image: ghcr.io/imgproxy/imgproxy:latest
restart: unless-stopped
ports:
- "127.0.0.1:8080:8080"
env_file:
- .env
sudo docker compose up -d sudo docker compose ps
قرار دادن تصاویر اصلی روی Origin
مثلاً تصویر زیر را در Origin قرار میدهیم:
/srv/file-platform/images/product.jpg
و از طریق این URL قابل دسترسی است:
https://origin.example.com/images/product.jpg
تست Resize تصویر
در محیط آزمایشی و بدون Signature، ساختار URL imgproxy شبیه این است:
/SIGNATURE/rs:fill:800:600/plain/SOURCE_URL@webp
اما در Production نباید Endpoint را با Signature غیرفعال یا insecure در اینترنت قرار دهید؛ چون هر فرد میتواند هزاران سایز مختلف درخواست کند و CPU سرور را مصرف کند.
ساخت URL امضاشده برای Image CDN
imgproxy از HMAC برای امضای مسیر URL استفاده میکند. یک Generator ساده Python:
import base64
import hashlib
import hmac
KEY = bytes.fromhex(
"YOUR_HEX_KEY"
)
SALT = bytes.fromhex(
"YOUR_HEX_SALT"
)
source = (
"https://origin.example.com"
"/images/product.jpg"
)
encoded_source = (
base64.urlsafe_b64encode(
source.encode()
)
.rstrip(b"=")
.decode()
)
path = (
"/rs:fill:800:600:0"
f"/{encoded_source}.webp"
)
digest = hmac.new(
KEY,
SALT + path.encode(),
hashlib.sha256
).digest()
signature = (
base64.urlsafe_b64encode(
digest
)
.rstrip(b"=")
.decode()
)
print(
"https://img.example.com/"
+ signature
+ path
)
هر بار که کاربر سایز 800×600 را درخواست کند، همان URL تولید میشود؛ بنابراین نتیجه بهخوبی قابل Cache شدن است.
Cache کردن خروجیهای Image CDN
اگر imgproxy هر بار تصویر را دوباره Resize کند، CPU بیهوده مصرف میشود. بهترین حالت این است که خروجیهای پردازششده در Nginx Cache شوند.
در nginx.conf:
proxy_cache_path \ /var/cache/nginx/images \ levels=1:2 \ keys_zone=IMAGE_CACHE:100m \ max_size=30g \ inactive=30d \ use_temp_path=off;
Virtual Host:
server {
listen 80;
server_name img.example.com;
location / {
proxy_cache IMAGE_CACHE;
proxy_cache_key \
"$scheme$host$request_uri";
proxy_cache_lock on;
proxy_cache_valid 200 30d;
proxy_cache_valid 404 1m;
proxy_cache_use_stale \
error \
timeout \
updating;
proxy_pass \
http://127.0.0.1:8080;
add_header \
X-Image-Cache \
$upstream_cache_status \
always;
}
}
WebP بهتر است یا AVIF؟
imgproxy میتواند خروجیهای WebP و AVIF تولید کند. AVIF در بسیاری از تصاویر حجم کمتری ایجاد میکند، اما Encoding آن معمولاً CPU بیشتری میطلبد.
برای شروع میتوانید URLهای مشخص و پایدار تولید کنید:
product-800x600.webp product-800x600.avif
اگر انتخاب Format را براساس Header مرورگر انجام میدهید، Cache Key باید تفاوت Accept Header را هم درست مدیریت کند. برای معماری ساده، URL مستقل برای هر Format قابل پیشبینیتر است.
استفاده از Image CDN در HTML سایت
بعد از ساخت URLهای امضاشده میتوانید srcset ایجاد کنید:
<img
src="IMAGE_800_URL"
srcset="
IMAGE_400_URL 400w,
IMAGE_800_URL 800w,
IMAGE_1200_URL 1200w
"
sizes="(max-width: 600px) 100vw, 800px"
loading="lazy"
alt="Product image"
/>
مرورگر متناسب با عرض نمایشگر و Device Pixel Ratio نسخه مناسبتر را انتخاب میکند.
امنیت CDN شخصی
CDN شخصی یعنی خودتان مسئول امنیت Origin، Edge و Image Processor هستید.
✅ Origin را تا حد امکان فقط برای Edgeها باز کنید.
✅ HTTPS را روی Origin و Edge فعال کنید.
✅ Upload API را بدون Authentication عمومی نکنید.
✅ Tokenهای انتقال فایل را طولانی و تصادفی بسازید.
✅ URLهای imgproxy را Sign کنید.
✅ فایلهای موقت منقضیشده را خودکار پاک کنید.
✅ روی Cache Disk فضای آزاد را مانیتور کنید.
✅ Docker Container را مستقیماً روی IP عمومی Publish نکنید.
چطور Origin را پنهان کنیم؟
اگر کاربران بتوانند مستقیماً Origin را باز کنند، ممکن است Cache و Edge را دور بزنند.
برای معماری حرفهایتر میتوانید Origin را فقط برای IP Edge Nodeها Allow کنید. یک روش دیگر قرار دادن Origin در Private Network میان VPSها است.
اگر Edge و Origin در دیتاسنترهایی هستند که شبکه خصوصی بین آنها در دسترس نیست، حداقل Firewall Origin را روی IPهای مشخص Edge محدود کنید.
چطور Cache را بهروزرسانی کنیم؟
در نسخه متنباز Nginx بهترین استراتژی برای Assetهای استاتیک این است که بهجای Purge مداوم، URL فایل را Version کنید.
یعنی بهجای:
/static/app.js
نسخه جدید را با URL جدید منتشر کنید:
/static/app.92db18.js
فایل قبلی تا پایان TTL در Cache میماند، اما دیگر در صفحات جدید Reference نمیشود.
چه چیزهایی را روی CDN مانیتور کنیم؟
فقط Up/Down بودن Nginx کافی نیست. چند شاخص مهم عبارتاند از:
🔹 Cache HIT Ratio
🔹 مصرف Disk Cache
🔹 Traffic خروجی Edge
🔹 Traffic ورودی به Origin
🔹 Response Time
🔹 خطاهای 4xx و 5xx
🔹 CPU مصرفی imgproxy
🔹 تعداد فایلهای موقت منقضیشده
آیا CDN شخصی از CDN تجاری ارزانتر است؟
همیشه نه. اگر ترافیک کم باشد، چند VPS ممکن است از نظر هزینه قابل قبول باشند؛ اما با افزایش تعداد کشورها، Traffic، Storage و نیاز به DDoS Protection، مدیریت شبکه شخصی پیچیدهتر میشود.
مزیت CDN شخصی بیشتر در کنترل کامل، Privacy، سناریوهای داخلی، فایلهای خاص، Lab، دانلودسنتر اختصاصی و معماری Hybrid دیده میشود.
برای وبسایت جهانی با میلیونها کاربر، استفاده از CDN تخصصی همچنان میتواند منطقیتر باشد.
این معماری برای چه پروژههایی مناسب است؟
🔹 فایلهای دانلود مشتریان
🔹 انتقال فایل موقت میان تیمها
🔹 فایلهای CSS و JavaScript سایت
🔹 فونت و Assetهای استاتیک
🔹 تصاویر فروشگاه اینترنتی
🔹 تصاویر وبلاگ یا اپلیکیشن
🔹 دانلودسنتر نرمافزار
🔹 سرویس داخلی سازمان
رفع مشکلات رایج
X-Cache-Status همیشه MISS است
Cache-Control، Set-Cookie، Vary Header، Cache Key و Permission مسیر /var/cache/nginx را بررسی کنید. برخی Responseها طبق Header مبدا قابل Cache نیستند.
Edge فایل قدیمی نمایش میدهد
URL Asset را Version کنید. تغییر محتوا زیر URL ثابت همراه با TTL طولانی طراحی مناسبی برای CDN نیست.
Image CDN CPU زیادی مصرف میکند
بررسی کنید خروجی imgproxy Cache میشود یا نه. اگر هر Request دوباره پردازش شود، CPU بهسرعت بالا میرود.
فایل موقت Download نمیشود
زمان انقضای Token، Permission فایل، Location داخلی Nginx و X-Accel-Redirect را بررسی کنید.
Upload فایل بزرگ با 413 قطع میشود
client_max_body_size در Nginx و MAX_BYTES در Application را بررسی کنید. هر دو باید با سیاست Upload شما هماهنگ باشند.
imgproxy خطای Signature میدهد
KEY و SALT برنامه تولید URL باید دقیقاً با Environment Container یکسان باشند. همچنین Signature باید روی همان Path دقیق URL محاسبه شود.
سؤالات متداول درباره CDN شخصی و سرویس انتقال فایل
آیا با یک VPS میتوان CDN ساخت؟
با یک VPS میتوانید Origin یا Cache Proxy بسازید، اما CDN واقعی معمولاً چند Edge Node در نقاط مختلف و مکانیزم مسیریابی کاربران میان آنها دارد.
CDN شخصی با Nginx چگونه کار میکند؟
Nginx Edge اولین درخواست فایل را از Origin میگیرد و در Cache محلی ذخیره میکند. درخواستهای بعدی همان URL مستقیماً از Edge پاسخ داده میشوند.
سرویس انتقال فایل موقت چگونه فایل را حذف میکند؟
برای هر فایل زمان انقضا در دیتابیس ذخیره میشود و یک Cleanup Job دورهای فایل و رکوردهای منقضیشده را حذف میکند.
Image CDN چه تفاوتی با Static CDN دارد؟
Static CDN فایل موجود را Cache میکند، اما Image CDN میتواند قبل از Cache کردن تصویر را Resize، Crop، Optimize یا به WebP و AVIF تبدیل کند.
imgproxy چیست؟
imgproxy یک سرویس مستقل پردازش تصویر است که میتواند تصویر Source را دریافت کند و براساس پارامترهای URL نسخه Resize یا تبدیلشده تحویل دهد.
چرا URLهای imgproxy باید Sign شوند؟
اگر URL آزاد باشد، مهاجم میتواند تعداد بسیار زیادی سایز و تبدیل مختلف درخواست کند و CPU سرور پردازش تصویر را مصرف کند. Signature فقط URLهای تولیدشده توسط Application شما را معتبر میکند.
آیا Nginx میتواند هنگام خرابی Origin فایل Cacheشده را نمایش دهد؟
بله. proxy_cache_use_stale میتواند در برخی خطاها یا Timeoutهای Origin نسخه Cacheشده قبلی را تحویل دهد.
بهترین سیستمعامل برای CDN شخصی چیست؟
برای این معماری Ubuntu Server یا Debian انتخابهای سادهای هستند؛ Nginx، Python، Docker و ابزارهای مورد نیاز روی هر دو در دسترساند.
برای ساخت Origin، Edge یا دانلودسنتر به VPS نیاز دارید؟
میتوانید Origin را روی VPS ایران قرار دهید و برای تست معماری Multi-Edge از سرورهای مجازی دیگر استفاده کنید. اگر در انتخاب منابع، Nginx Cache، Docker یا شبکه میان Edgeها مشکل دارید، امکان درخواست کانفیگ نیز وجود دارد.
📌 تماس با پشتیبانی:
021-91302460
| ایرانیکاسرور
جمعبندی
با ترکیب Nginx، Python و imgproxy میتوان یک زیرساخت شخصی برای تحویل فایل ساخت که سه نیاز متفاوت را پوشش دهد: فایلهای موقت، Assetهای استاتیک و تصاویر بهینهشده.
FastAPI مسئول صدور لینکهای موقت است، Nginx فایلهای استاتیک را روی Edge Cache میکند و imgproxy نسخههای مختلف تصویر را فقط هنگام نیاز میسازد. با Cache کردن خروجی imgproxy نیز پردازش تکراری تصویر به حداقل میرسد.
اگر فقط یک Node داشته باشید، معماری شما هنوز بیشتر شبیه Origin + Cache Server است. با اضافه کردن Edge در چند نقطه و مسیریابی کاربران به Edge مناسب، این ساختار به یک CDN شخصی واقعی نزدیک میشود.
برای پروژههای داخلی، سرویسهای سازمانی، دانلودسنتر، فروش فایل یا سایتهایی که کنترل کامل روی داده و Cache برایشان مهم است، این معماری میتواند جایگزین جذابی برای قرار دادن تمام فایلها روی همان وبسرور اصلی باشد.






دیدگاهها (۰)