Construiește propriul server CDN: aduci o dată, livrezi de mii de ori
Un server CDN aduce fiecare fișier o dată de la origine și apoi îl livrează singur de mii de ori. Serverul de origine vede doar o fracțiune din cereri, iar vizitatorii primesc timpi de încărcare scurți. Așa construiești unul.
Fiecare fișier pe care un vizitator îl încarcă de pe site-ul tău este scos de serverul de origine din sistemul de fișiere, împachetat într-un răspuns și trimis la drum. La zece vizitatori pe zi nu observă nimeni asta. La zece mii de vizitatori simultani, exact acesta este motivul pentru care site-ul devine lent, deși aplicația propriu-zisă nu are nimic de făcut.
Un server CDN se așază în fața lui. Aduce fiecare fișier o singură dată de la origine și apoi îl livrează singur, de câte ori este nevoie. Originea vede astfel doar o fracțiune din cereri, iar vizitatorii primesc fișierele de la un server care nu face nimic altceva decât să livreze. Articolul arată cât aduce asta în realitate, cum ridici un astfel de server cu nginx și ce contează la trafic și la locație.
Ce face în realitate un server CDN
Mersul lucrurilor se spune în trei propoziții:
- Un vizitator cere un fișier de la serverul CDN.
- Dacă acesta se află deja acolo, pleacă imediat. Acesta este un hit.
- Dacă nu se află acolo, serverul CDN îl aduce de la origine, îl depune și îl livrează. Acesta este un miss și se întâmplă pentru fiecare fișier o singură dată pe durată de valabilitate.
Principiul este același ca la un server de streaming: aduci o dată, livrezi de multe ori. Diferența este doar că un server de streaming distribuie un flux de date în curs, iar un server CDN fișiere gata făcute.
Socoteala: cât de mult ușurează un cache
Decisivă este rata de hit, adică partea din cereri la care răspunde cache-ul singur. La un milion de cereri pe zi, reducerea sarcinii arată așa:
| Rata de hit | Cereri la origine | Reducerea sarcinii |
|---|---|---|
| 50% | 500.000 | factor 2 |
| 80% | 200.000 | factor 5 |
| 95% | 50.000 | factor 20 |
| 99% | 10.000 | factor 100 |
Pentru fișierele statice, 95 la sută și peste este cazul normal, pentru că imaginile, scripturile și fonturile se schimbă rar. Saltul de la 95 la 99 la sută este mai valoros decât lasă să se creadă cifrele: împarte încă o dată la cinci sarcina rămasă.
În același timp, traficul se mută. Originea nu mai transferă aproape nimic, în schimb serverul CDN cu atât mai mult. La 500.000 de accesări de imagini pe zi, cu 400 KB fiecare, asta înseamnă 200 GB zilnic, deci în jur de 6 TB pe lună, numai pentru imagini. De aceea, la un nod CDN cifra importantă nu este procesorul, ci conectivitatea și volumul.
Când merită un server CDN propriu
Furnizorii comerciali de CDN facturează pe gigabyte transferat. Este comod și, la volum mic, ieftin. De la o anumită cantitate, raportul se inversează:
| Caz | Server CDN propriu | Facturare pe gigabyte |
|---|---|---|
| Câțiva GB pe lună | aproape că nu merită | mai ieftină |
| Mulți TB pe lună | preț lunar fix | crește liniar |
| Descărcări mari, arhive video | clar în avantaj | devine repede scumpă |
| Control deplin asupra regulilor și protocoalelor | complet | limitat |
Mai apare un punct greu de pus în cifre: la un nod propriu decizi tu ce se pune în cache, cât timp și când dispare. Nu ești legat de regulile unui furnizor străin și poți livra și lucruri care nu se potrivesc în scheme străine.
Montajul cu nginx pe post de cache
Instalare
apt update
apt install -y nginx
Nu trebuie mai mult, cache-ul este integrat în nginx. Creează directorul în care să stea fișierele:
mkdir -p /var/cache/cdn
chown www-data:www-data /var/cache/cdn
Configurare
Cache-ul se descrie o dată în blocul http și apoi se folosește în blocurile location individuale:
proxy_cache_path /var/cache/cdn
levels=1:2
keys_zone=cdn:100m
max_size=200g
inactive=30d
use_temp_path=off;
proxy_cache_key "$scheme$request_method$host$request_uri";
Valorile înseamnă, pe rând: keys_zone=cdn:100m ajunge pentru circa 800.000 de intrări, max_size limitează spațiul pe disc, inactive=30d aruncă fișierele pe care nu le-a cerut nimeni timp de 30 de zile, iar use_temp_path=off evită o mutare inutilă între directoare.
Dedesubt, serverul propriu-zis:
server {
listen 443 ssl;
http2 on;
server_name cdn.example.com;
ssl_certificate /etc/letsencrypt/live/cdn.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/cdn.example.com/privkey.pem;
location / {
proxy_pass https://origin.example.com;
proxy_set_header Host origin.example.com;
proxy_http_version 1.1;
proxy_cache cdn;
proxy_cache_valid 200 301 302 24h;
proxy_cache_valid 404 1m;
# mai multe miss-uri simultane se strâng într-o singură cerere
proxy_cache_lock on;
proxy_cache_lock_timeout 10s;
# la probleme la origine se livrează mai departe
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
# cookie-urile de sesiune nu trebuie să spargă cache-ul
proxy_ignore_headers Set-Cookie;
proxy_hide_header Set-Cookie;
add_header X-Cache-Status $upstream_cache_status always;
}
}
Trei linii de aici merită atenție deosebită. proxy_cache_lock face ca, la un fișier nou, să nu ceară o sută de vizitatori originea în același timp, ci doar unul, iar ceilalți să aștepte. proxy_cache_use_stale menține livrarea în timp ce originea repornește sau răspunde încet. Iar ascunderea lui Set-Cookie împiedică un singur răspuns cu cookie de sesiune să facă tot cache-ul inutilizabil.
Verifici dacă prinde
nginx -t && systemctl reload nginx
curl -sI https://cdn.example.com/bild.jpg | grep -i x-cache-status
curl -sI https://cdn.example.com/bild.jpg | grep -i x-cache-status
La primul apel scrie acolo MISS, la al doilea HIT. Dacă și a doua dată scrie MISS, aproape mereu este unul dintre cei trei suspecți obișnuiți: un Set-Cookie în răspuns, un Cache-Control: no-store de la origine sau o lungime care lipsește.
Ce se pune în cache și ce nu
Soluția curată este ca originea să spună singură. Ea își cunoaște conținutul, cache-ul nu. Pentru asta pune headerele potrivite:
| Conținut | Header la origine |
|---|---|
| Fișier cu sumă de control în nume | Cache-Control: public, max-age=31536000, immutable |
| Imagine sau descărcare fără versionare | Cache-Control: public, max-age=86400 |
| HTML-ul unei pagini publice | Cache-Control: public, max-age=300 |
| Zonă autentificată, coș de cumpărături | Cache-Control: private, no-store |
Dacă vrei să decizi în cache însuși, asta merge prin blocuri location proprii pentru fiecare tip de fișier:
location ~* \.(jpg|jpeg|png|gif|webp|avif|svg|ico|css|js|woff2|mp4|zip)$ {
proxy_pass https://origin.example.com;
proxy_cache cdn;
proxy_cache_valid 200 7d;
add_header Cache-Control "public, max-age=604800";
add_header X-Cache-Status $upstream_cache_status always;
}
Scoți conținut în mod țintit
Calea cea mai simplă este să nu fie nevoie să ștergi deloc: cine dă fiecărei imagini schimbate un nume nou de fișier, de pildă logo.a1b2c3.png, nu are problema asta. Fișierul vechi cade cândva singur din cache.
Dacă nu se poate, la nginx ajută modulul liber ngx_cache_purge. Fără modul suplimentar, calea se poate calcula din cheia de cache, pentru că nginx folosește suma MD5 a acesteia ca nume de fișier:
KEY="httpsGETcdn.example.com/bild.jpg"
H=$(printf '%s' "$KEY" | md5sum | cut -d' ' -f1)
rm -f "/var/cache/cdn/${H: -1}/${H: -3:2}/$H"
Pentru uzul zilnic ajunge de obicei un script mic, care execută aceste linii pentru o listă de căi.
Fișiere mari și videoclipuri
La fișiere de peste câteva sute de megabytes ai nevoie de descompunerea în blocuri, altfel cache-ul încarcă din nou fișierul întreg la fiecare salt în video:
location /video/ {
proxy_pass https://origin.example.com;
proxy_cache cdn;
proxy_cache_valid 200 206 7d;
slice 1m;
proxy_set_header Range $slice_range;
proxy_cache_key "$uri$slice_range";
}
Astfel nginx aduce doar blocurile care sunt cerute cu adevărat. Dacă un spectator sare în mijlocul unui film, pe linie trec câțiva megabytes în loc de mai mulți gigabytes.
TLS, HTTP/2 și HTTP/3
Un nod CDN livrează multe fișiere mici către multe conexiuni, exact acolo se amortizează protocoalele mai noi. HTTP/2 este rezolvat cu http2 on;. HTTP/3 aduce ceva mai ales pe conexiunile mobile, pentru că o schimbare de rețea nu lasă conexiunea să se rupă:
listen 443 quic reuseport;
listen 443 ssl;
http2 on;
add_header Alt-Svc 'h3=":443"; ma=86400';
Nu uita să deschizi UDP 443 în firewall. Acesta este cel mai frecvent motiv pentru care HTTP/3 este configurat și totuși nimeni nu îl folosește.
Mai multe locații
Un singur nod aduce deja cea mai mare parte din câștig, anume ușurarea originii. Al doilea câștig, drumuri scurte către vizitator, vine abia cu mai multe locații.
Distribuția merge prin DNS: același nume arată, în funcție de proveniența vizitatorului, către un alt nod. Pentru asta ai nevoie de un furnizor DNS cu atribuire geografică. Toate nodurile primesc aceeași configurație și aceeași origine, nu știu nimic unul despre altul, și exact asta face montajul atât de robust: dacă un nod cade, îl scoți din DNS, iar celelalte preiau.
KernelHost oferă locații în Germania, pe Coasta de Vest a SUA, pe Coasta de Est a SUA, precum și în Singapore și Coreea de Sud. O privire de ansamblu cu latențe găsești la locațiile serverelor. Ca regulă de bază: un nod pe fiecare continent în care stă o parte demnă de menționat din publicul tău.
Măsori, nu ghicești
Scrie starea cache-ului în jurnal, apoi poți număra rata de hit oricând:
log_format cdn '$remote_addr $status $body_bytes_sent '
'$upstream_cache_status "$request"';
access_log /var/log/nginx/cdn.log cdn;
Se poate evalua cu o singură linie:
awk '{print $4}' /var/log/nginx/cdn.log | sort | uniq -c | sort -rn
Dacă partea de HIT la fișierele statice stă sub 90 la sută, căutarea cauzei merită aproape mereu: de obicei sunt cookie-uri în răspunsuri, valabilități prea scurte sau cereri cu parametri schimbători la capătul adresei, care arată pentru cache de fiecare dată ca un fișier nou.
Ce server se potrivește
Un nod CDN este o mașină de livrare. Ordinea proprietăților importante arată de aceea altfel decât la un server de aplicație:
- Trafic și conectivitate. Aici apare toată sarcina. Un nod transferă în funcționare, în mod regulat, mai mulți terabytes pe lună.
- Discul. NVMe este clar în avantaj, pentru că cache-ul este făcut din multe accesări mici și aleatorii. Mărimea se orientează după cât din fondul tău este fierbinte, nu după cât de mare este fondul în total.
- RAM. Sistemul de operare folosește memoria liberă ca al doilea cache în fața discului. Mai mult RAM înseamnă aici direct mai multă viteză.
- Procesorul. Cel mai puțin critic, este folosit doar pentru TLS, iar asta o rezolvă procesoarele moderne în treacăt.
Exact pentru acest caz sunt făcute serverele root cu trafic nelimitat de la noi: 2 × 1 Gbit/s reali pe server, fără limitare de viteză, volum nelimitat la preț lunar fix, NVMe de centru de date în RAID și 3,2 Tbps protecție antiflood din fabrică. Livrarea durează mai puțin de 30 de secunde, cu acces root complet, IPv4 și o rețea IPv6 /64. Pentru că facturarea se face pe bază PrePaid și se poate anula lunar, poți începe cu un nod și adăuga oricând o a doua locație, atunci când publicul tău se împrăștie.
Întrebări frecvente
Ce face exact un server CDN?
Cât de mult ușurează asta în realitate?
Am nevoie pentru asta de un furnizor comercial?
Ce fișiere să pun în cache și ce nu?
Cât timp să păstreze cache-ul fișierele?
Cum șterg un fișier din cache?
Ce se întâmplă dacă serverul de origine cade?
Funcționează și pentru fișiere video mari?
De câte locații am nevoie?
Cum recunosc că cache-ul funcționează?
2026 KernelHost GmbH. Toate drepturile rezervate. Acest ghid este protejat de legea drepturilor de autor. Republicarea lui pe alte site-uri, integral, parțial sau în formă modificată, nu este permisă fără acordul nostru scris. Citatele cu indicarea sursei și cu link sunt binevenite.

