Εισαγωγή: Γιατί το TTFB Παραμένει Κρίσιμο το 2026
Αν ασχολείστε με web performance, ξέρετε ήδη ότι κάθε millisecond μετράει. Το TTFB (Time to First Byte) -- ο χρόνος από τη στιγμή που ο browser στέλνει ένα HTTP request μέχρι να λάβει το πρώτο byte της απάντησης -- είναι ουσιαστικά ο θεμέλιος λίθος πάνω στον οποίο χτίζεται ολόκληρη η εμπειρία φόρτωσης μιας σελίδας.
Όσο πιο αργό το TTFB, τόσο πιο αργά ξεκινάει τα πάντα: parsing του HTML, downloading των resources, rendering του content.
Το 2026, η Google συνεχίζει να συνιστά TTFB κάτω από 800ms στο 75ο εκατοστημόριο (p75) των χρηστών σας. Αν και το TTFB δεν είναι από μόνο του Core Web Vital, επηρεάζει άμεσα το LCP (Largest Contentful Paint), που είναι Core Web Vital. Σκεφτείτε το λίγο: αν ο server σας χρειάζεται 1.5 δευτερόλεπτα για να απαντήσει, είναι μαθηματικά αδύνατο να πετύχετε LCP κάτω από 2.5 δευτερόλεπτα -- το όριο "good" της Google.
Μελέτες δείχνουν ότι μείωση του TTFB κατά 100ms μπορεί να βελτιώσει το LCP κατά 50-150ms, ανάλογα με τη σελίδα. Αξίζει τον κόπο, σίγουρα.
Λοιπόν, σε αυτόν τον οδηγό θα εξετάσουμε κάθε τεχνική βελτιστοποίησης TTFB που έχετε στη διάθεσή σας το 2026: από CDN και HTTP/3, μέχρι 103 Early Hints, streaming SSR, και πολυεπίπεδες στρατηγικές caching. Με πρακτικά παραδείγματα κώδικα και πραγματικά δεδομένα.
Τι Είναι το TTFB και Πώς Μετριέται
Τα Συστατικά Στοιχεία του TTFB
Το TTFB δεν είναι ένα μονοδιάστατο μέτρο -- αποτελείται από πολλαπλά στάδια, καθένα από τα οποία μπορεί να γίνει bottleneck:
- DNS Lookup: Η αναζήτηση της IP διεύθυνσης του server (τυπικά 20-120ms, αλλά μπορεί να φτάσει 200ms+ σε uncached queries)
- TCP Connection: Το three-way handshake για τη δημιουργία σύνδεσης (1 RTT)
- TLS Handshake: Η διαπραγμάτευση κρυπτογράφησης για HTTPS (1-2 RTTs ανάλογα με την έκδοση TLS)
- Server Processing Time: Ο χρόνος που χρειάζεται ο server για να επεξεργαστεί το request και να ξεκινήσει την αποστολή δεδομένων
- Network Latency: Ο χρόνος μεταφοράς δεδομένων μέσω του δικτύου, που εξαρτάται από τη γεωγραφική απόσταση
Σε μια τυπική HTTPS σύνδεση με HTTP/2, χρειάζονται τουλάχιστον 3 RTTs πριν αρχίσει η μεταφορά δεδομένων.
Αν ο χρήστης είναι στην Αθήνα και ο server στη Νέα Υόρκη (RTT ~120ms), μιλάμε για 360ms+ μόνο από overhead σύνδεσης, χωρίς καν να υπολογίσουμε τον χρόνο επεξεργασίας του server. Και αυτό είναι η καλύτερη περίπτωση.
Εργαλεία Μέτρησης
Για ακριβή μέτρηση του TTFB, έχετε αρκετές επιλογές:
- Chrome DevTools: Στο tab Network, κάθε request εμφανίζει το "Waiting for server response" (TTFB) στο waterfall
- WebPageTest: Παρέχει λεπτομερή ανάλυση κάθε στοιχείου του TTFB (DNS, Connect, TLS, Wait) με δοκιμές από πολλαπλές τοποθεσίες παγκοσμίως
- Lighthouse: Αναφέρει το TTFB ως μέρος της αξιολόγησης performance και σημειώνει αν υπερβαίνει τα 800ms
- CrUX (Chrome User Experience Report): Πραγματικά δεδομένα RUM από χρήστες Chrome -- ειλικρινά, αυτό είναι το πιο αξιόπιστο αν θέλετε πραγματικά νούμερα
Μέτρηση TTFB μέσω JavaScript
Η πιο ακριβής μέθοδος μέτρησης TTFB στον browser είναι μέσω του PerformanceNavigationTiming API:
// Μέτρηση TTFB με το Navigation Timing API
function measureTTFB() {
const [navigation] = performance.getEntriesByType('navigation');
if (navigation) {
// Συνολικό TTFB
const ttfb = navigation.responseStart - navigation.requestStart;
// Ανάλυση επιμέρους στοιχείων
const dnsTime = navigation.domainLookupEnd - navigation.domainLookupStart;
const tcpTime = navigation.connectEnd - navigation.connectStart;
const tlsTime = navigation.requestStart - navigation.secureConnectionStart;
const serverTime = navigation.responseStart - navigation.requestStart;
console.log(`TTFB: ${ttfb.toFixed(2)}ms`);
console.log(`DNS: ${dnsTime.toFixed(2)}ms`);
console.log(`TCP: ${tcpTime.toFixed(2)}ms`);
console.log(`TLS: ${tlsTime.toFixed(2)}ms`);
console.log(`Server Processing: ${serverTime.toFixed(2)}ms`);
// Αποστολή στο analytics endpoint σας
sendToAnalytics({
ttfb, dnsTime, tcpTime, tlsTime, serverTime,
url: navigation.name,
protocol: navigation.nextHopProtocol // "h2", "h3" κλπ.
});
}
}
// Εκτέλεση μετά τη φόρτωση της σελίδας
if (document.readyState === 'complete') {
measureTTFB();
} else {
window.addEventListener('load', measureTTFB);
}
Βελτιστοποίηση Server-Side Response Time
Ο χρόνος επεξεργασίας στον server είναι συχνά ο μεγαλύτερος παράγοντας του TTFB. Και ειλικρινά, εδώ είναι που οι περισσότερες ομάδες μπορούν να κερδίσουν τα μεγαλύτερα κέρδη.
Βελτιστοποίηση Database Queries
Τα αργά database queries είναι η νούμερο ένα αιτία υψηλού server processing time. Το βλέπω ξανά και ξανά σε projects -- ένα μόνο κακοφτιαγμένο query μπορεί να σκοτώσει ολόκληρο το TTFB. Βεβαιωθείτε ότι:
- Χρησιμοποιείτε κατάλληλα indexes για κάθε query pattern
- Αποφεύγετε τα N+1 queries -- χρησιμοποιήστε eager loading ή batching
- Χρησιμοποιείτε connection pooling για να αποφύγετε το overhead δημιουργίας νέων connections
- Σκεφτείτε read replicas για read-heavy workloads
- Χρησιμοποιείτε query result caching για δεδομένα που δεν αλλάζουν συχνά
Στρατηγικές Caching στον Server
Το caching είναι η πιο αποτελεσματική τεχνική μείωσης του server processing time. Δεν υπερβάλλω.
Ας δούμε ένα ολοκληρωμένο παράδειγμα με Redis:
// Παράδειγμα Redis caching pattern σε Node.js
import Redis from 'ioredis';
const redis = new Redis({
host: 'localhost',
port: 6379,
maxRetriesPerRequest: 3,
retryDelayOnFailover: 100
});
// Cache-aside pattern με αυτόματη ανανέωση
async function getCachedData(key, fetchFn, ttlSeconds = 300) {
try {
// 1. Αναζήτηση στο cache
const cached = await redis.get(key);
if (cached) {
return JSON.parse(cached);
}
// 2. Αν δεν υπάρχει, φόρτωση από την πηγή
const data = await fetchFn();
// 3. Αποθήκευση στο cache με TTL
await redis.set(key, JSON.stringify(data), 'EX', ttlSeconds);
return data;
} catch (error) {
// Fallback: αν το Redis είναι εκτός λειτουργίας, πήγαινε κατευθείαν στην πηγή
console.error('Redis error:', error.message);
return fetchFn();
}
}
// Χρήση στο route handler
app.get('/api/products/:category', async (req, res) => {
const { category } = req.params;
const cacheKey = `products:${category}:v2`;
const products = await getCachedData(
cacheKey,
() => db.products.findMany({ where: { category } }),
600 // 10 λεπτά cache
);
res.json(products);
});
HTTP Caching Headers
Οι σωστές Cache-Control headers μπορούν να εξαλείψουν τελείως την ανάγκη για server round-trips:
# Nginx configuration για HTTP caching headers
# Στατικά assets - μεγάλο cache με immutable
location ~* \.(js|css|png|jpg|jpeg|webp|avif|woff2|ico)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Vary "Accept-Encoding";
}
# HTML σελίδες - σύντομο cache με revalidation
location ~* \.html$ {
add_header Cache-Control "public, max-age=300, stale-while-revalidate=86400";
}
# API responses - no-cache αλλά αποθήκευση με revalidation
location /api/ {
add_header Cache-Control "private, no-cache, must-revalidate";
add_header Vary "Authorization, Accept";
}
Connection Pooling και Keep-Alive
Η ενεργοποίηση HTTP Keep-Alive μειώνει τον χρόνο σύνδεσης για διαδοχικά requests. Ένα από αυτά τα πράγματα που αν δεν τα ρυθμίσετε σωστά, πληρώνετε σε TTFB χωρίς να το καταλαβαίνετε.
Στον Nginx:
# Nginx Keep-Alive ρυθμίσεις
http {
# Keep-Alive προς τους clients
keepalive_timeout 65;
keepalive_requests 1000;
upstream backend {
server 127.0.0.1:3000;
# Keep-Alive προς τον backend server
keepalive 32;
keepalive_timeout 60s;
keepalive_requests 100;
}
server {
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
}
CDN και Edge Computing
Αν ο server σας βρίσκεται στο Λονδίνο και ένας χρήστης σας είναι στο Τόκυο, κανένα ποσό server-side optimization δεν θα εξαλείψει τα ~250ms RTT λόγω γεωγραφικής απόστασης. Η φυσική δεν συγχωρεί.
Εδώ μπαίνει το CDN (Content Delivery Network).
Πώς τα CDN Μειώνουν το TTFB
Τα CDN διατηρούν αντίγραφα του content σας σε εκατοντάδες Points of Presence (PoPs) παγκοσμίως. Όταν ένας χρήστης ζητάει τη σελίδα σας, εξυπηρετείται από τον πλησιέστερο PoP, μειώνοντας δραστικά τα RTTs.
Ένα σωστά ρυθμισμένο CDN μπορεί να μειώσει το TTFB από 800ms+ σε κάτω από 100ms για cached content. Η διαφορά είναι κυριολεκτικά νύχτα με μέρα.
Edge Computing το 2026
Τo 2026, τα CDN δεν είναι πλέον απλά "αποθήκες αντιγράφων". Σύμφωνα με αναλύσεις, περίπου 50% των νέων deployments υποδομών χρησιμοποιούν edge locations. Οι σύγχρονες CDN πλατφόρμες προσφέρουν:
- Edge Compute: Εκτέλεση κώδικα στο edge (Cloudflare Workers, AWS Lambda@Edge, Deno Deploy)
- AI-powered routing: Ευφυής δρομολόγηση traffic με βάση real-time δεδομένα δικτύου
- Edge databases: Βάσεις δεδομένων στο edge (Cloudflare D1, Turso, Neon) για ακόμη χαμηλότερο latency
- Edge KV stores: Κατανεμημένα key-value stores σε κάθε PoP
Πρακτικό Παράδειγμα: Cloudflare Workers
Ας δούμε ένα παράδειγμα edge function που εξυπηρετεί δυναμικό content με caching στο edge:
// Cloudflare Worker: Edge SSR με caching
export default {
async fetch(request, env) {
const url = new URL(request.url);
const cacheKey = new Request(url.toString(), request);
const cache = caches.default;
// 1. Έλεγχος edge cache
let response = await cache.match(cacheKey);
if (!response) {
// 2. Δημιουργία απάντησης στο edge
const data = await env.DB.prepare(
'SELECT * FROM products WHERE category = ?'
).bind(url.searchParams.get('cat')).all();
const html = renderProductPage(data.results);
response = new Response(html, {
headers: {
'Content-Type': 'text/html; charset=utf-8',
'Cache-Control': 'public, max-age=300, s-maxage=600',
// Ενεργοποίηση stale-while-revalidate
'CDN-Cache-Control': 'public, max-age=600, stale-while-revalidate=3600',
// Early Hints για τα κρίσιμα resources
'Link': '</styles/main.css>; rel=preload; as=style, </fonts/inter.woff2>; rel=preload; as=font; crossorigin'
}
});
// 3. Αποθήκευση στο edge cache
request.method === 'GET' && event.waitUntil(cache.put(cacheKey, response.clone()));
}
return response;
}
};
function renderProductPage(products) {
return `<!DOCTYPE html>
<html lang="el">
<head>
<title>Προϊόντα</title>
<link rel="stylesheet" href="/styles/main.css">
</head>
<body>
<main>
${products.map(p => `
<article class="product">
<h2>${p.name}</h2>
<p>${p.description}</p>
<span class="price">${p.price}€</span>
</article>
`).join('')}
</main>
</body>
</html>`;
}
Με αυτή την προσέγγιση, ο χρόνος TTFB μπορεί να πέσει κάτω από 50ms για cached responses, αφού ο κώδικας εκτελείται στον πλησιέστερο PoP του χρήστη. Εντυπωσιακό, σωστά;
HTTP/3 και QUIC: Η Νέα Εποχή των Πρωτοκόλλων
Το HTTP/3, βασισμένο στο πρωτόκολλο QUIC, αντιπροσωπεύει τη μεγαλύτερη αλλαγή στα πρωτόκολλα web μεταφοράς εδώ και μια δεκαετία. Και δεν το λέω για dramatic effect -- πραγματικά αλλάζει τα δεδομένα.
Το 2026, η παγκόσμια υιοθέτηση του HTTP/3 έχει φτάσει περίπου στο 35% του web traffic, με σταθερή ανοδική τάση.
Γιατί το HTTP/3 Μειώνει το TTFB
Τα πλεονεκτήματα του QUIC/HTTP/3 σε σχέση με το TTFB:
- Ελαττωμένα RTTs σύνδεσης: Το QUIC συνδυάζει transport και crypto handshake σε ένα βήμα, μειώνοντας τον χρόνο δημιουργίας σύνδεσης κατά περίπου 33% σε σχέση με TCP+TLS 1.3
- 0-RTT Connection Resumption: Για επαναλαμβανόμενες συνδέσεις σε γνωστούς servers, τα δεδομένα μπορούν να σταλούν μαζί με το πρώτο πακέτο -- κυριολεκτικά μηδενικό overhead σύνδεσης
- Βελτιωμένη απόδοση σε lossy δίκτυα: Δεν υπάρχει head-of-line blocking σε επίπεδο transport, αφού κάθε stream είναι ανεξάρτητο
- Connection Migration: Η σύνδεση διατηρείται ακόμα και αν αλλάξει η IP (π.χ. εναλλαγή Wi-Fi σε cellular)
Σύμφωνα με αναφορά της Akamai (2025), το HTTP/3 παρέχει μέση μείωση latency 30% σε mobile συσκευές. Αυτό είναι ιδιαίτερα σημαντικό δεδομένου ότι η πλειονότητα του web traffic προέρχεται πλέον από κινητά.
Πότε το HTTP/3 Κάνει τη Μεγαλύτερη Διαφορά
Το HTTP/3 ωφελεί περισσότερο σε:
- Mobile συνδέσεις: Υψηλότερο latency, μεγαλύτερο packet loss
- Lossy δίκτυα: Σε δίκτυα με packet loss >1%, η διαφορά είναι θεαματική
- High-latency connections: Χρήστες γεωγραφικά μακριά από τον server
- Επαναλαμβανόμενες επισκέψεις: Όπου το 0-RTT resumption αξιοποιείται πλήρως
Ενεργοποίηση HTTP/3 στον Nginx
# Nginx configuration για HTTP/3 / QUIC
# Απαιτεί Nginx 1.25.0+ ή nginx-quic build
http {
# Ενεργοποίηση HTTP/3
server {
# HTTP/3 μέσω QUIC (UDP port 443)
listen 443 quic reuseport;
# HTTP/2 fallback μέσω TCP
listen 443 ssl;
http2 on;
http3 on;
ssl_certificate /etc/ssl/certs/example.com.pem;
ssl_certificate_key /etc/ssl/private/example.com.key;
# Ρυθμίσεις TLS 1.3 (απαραίτητο για HTTP/3)
ssl_protocols TLSv1.3;
ssl_early_data on; # 0-RTT support
# Ενημέρωση client ότι υποστηρίζεται HTTP/3
add_header Alt-Svc 'h3=":443"; ma=86400';
# QUIC-specific ρυθμίσεις
quic_retry on;
quic_gso on;
server_name example.com;
location / {
proxy_pass http://backend;
# Προστασία 0-RTT replay attacks
proxy_set_header Early-Data $ssl_early_data;
}
}
}
Σημαντική σημείωση: Το 0-RTT (early data) είναι ευάλωτο σε replay attacks. Βεβαιωθείτε ότι δεν χρησιμοποιείτε 0-RTT δεδομένα για non-idempotent operations -- δηλαδή POST requests που τροποποιούν δεδομένα. Αυτό δεν είναι κάτι που πρέπει να αγνοήσετε.
103 Early Hints: Προετοιμάστε τον Browser Πριν Ετοιμαστεί η Απάντηση
Τα 103 Early Hints είναι ένα informational HTTP response που ο server μπορεί να στείλει ενώ ακόμα επεξεργάζεται το κύριο request.
Η ιδέα είναι απλή αλλά ισχυρή: αντί ο browser να περιμένει ανενεργός κατά τη διάρκεια του server processing, μπορεί να αρχίσει να φορτώνει κρίσιμα resources εκ των προτέρων. Σαν να λέτε στον browser "θα αργήσω λίγο, αλλά στο μεταξύ ξεκίνα αυτά".
Πώς Λειτουργούν
Η ροή είναι η εξής:
- Ο browser στέλνει ένα HTTP request
- Ο server στέλνει αμέσως ένα
103 Early Hintsresponse μεLinkheaders - Ο browser αρχίζει να κάνει preload/preconnect τα υποδεικνυόμενα resources
- Ο server στέλνει τελικά το πλήρες
200 OKresponse
Αυτό "κρύβει" αποτελεσματικά τον server processing time πίσω από χρήσιμη δουλειά στον browser. Έξυπνο, σωστά;
Υποστήριξη Browsers
Η κατάσταση υποστήριξης το 2026:
- Chrome/Chromium: Πλήρης υποστήριξη
preloadκαιpreconnectμέσω Early Hints - Firefox: Υποστήριξη
preloadκαιpreconnect - Safari: Υποστήριξη μόνο
preconnect-- δεν υποστηρίζειpreloadμέσω Early Hints (γιατί, Apple;)
Ρύθμιση Early Hints στον Nginx
# Nginx: Ενεργοποίηση 103 Early Hints
server {
listen 443 ssl;
http2 on;
location / {
# Αποστολή Early Hints πριν την επεξεργασία
add_header Link "</css/critical.css>; rel=preload; as=style" early;
add_header Link "</fonts/inter-var.woff2>; rel=preload; as=font; crossorigin" early;
add_header Link "<https://api.example.com>; rel=preconnect" early;
proxy_pass http://backend;
}
}
Υλοποίηση σε Node.js
// Node.js: Early Hints με Express/HTTP native
import http from 'node:http';
const server = http.createServer(async (req, res) => {
// 1. Αποστολή 103 Early Hints αμέσως
res.writeEarlyHints({
'link': [
'</css/critical.css>; rel=preload; as=style',
'</js/app.js>; rel=preload; as=script',
'<https://cdn.example.com>; rel=preconnect; crossorigin'
]
});
// 2. Επεξεργασία του κύριου request (π.χ. SSR, database queries)
const data = await fetchPageData(req.url);
const html = await renderPage(data);
// 3. Αποστολή του τελικού response
res.writeHead(200, {
'Content-Type': 'text/html; charset=utf-8',
'Cache-Control': 'public, max-age=60'
});
res.end(html);
});
server.listen(3000);
Προσοχή: Early Hints και Mobile Performance
Σημαντική επιφύλαξη: Τα Early Hints μπορούν να χειροτερέψουν την απόδοση σε mobile συσκευές. Ναι, σωστά διαβάσατε.
Ο λόγος είναι ότι η επεξεργασία των Early Hints δημιουργεί overhead στον browser -- η έναρξη preload requests, η δημιουργία νέων connections, η ανάλυση headers. Αυτό το overhead είναι ιδιαίτερα προβληματικό σε mobile συσκευές με περιορισμένη επεξεργαστική ισχύ. Σε low-end devices, ο browser μπορεί να αναλώνει πολύτιμο processing time στη διαχείριση Early Hints αντί να είναι έτοιμος να αρχίσει rendering μόλις φτάσει το κύριο response.
Πριν ενεργοποιήσετε Early Hints, μετρήστε την πραγματική επίδραση τόσο σε desktop όσο και σε mobile χρήστες μέσω RUM δεδομένων. Μην υποθέτετε -- μετρήστε.
Streaming SSR και Partial Hydration
Η παραδοσιακή Server-Side Rendering έχει ένα βασικό πρόβλημα: ο server πρέπει να ολοκληρώσει πλήρως το rendering ολόκληρης της σελίδας πριν αρχίσει να στέλνει δεδομένα.
Αυτό σημαίνει ότι το TTFB εξαρτάται από το πιο αργό component στη σελίδα. Το πιο αργό query. Το πιο αργό API call. Ό,τι πάρει περισσότερο χρόνο, αυτό καθορίζει πότε θα δει κάτι ο χρήστης.
Streaming SSR: Στείλτε HTML σε Κομμάτια
Με Streaming SSR, ο server αρχίζει να στέλνει HTML αμέσως μόλις τα πρώτα components είναι έτοιμα, ενώ συνεχίζει να κάνει render τα υπόλοιπα παράλληλα. Αυτό δραστικά μειώνει το TTFB, αφού ο browser λαμβάνει τα πρώτα bytes πολύ νωρίτερα.
Παράδειγμα με Next.js App Router και Streaming
// app/products/page.tsx - Next.js streaming SSR
import { Suspense } from 'react';
// Αυτό το component φορτώνεται αμέσως (χαμηλό TTFB)
function PageHeader() {
return (
<header>
<h1>Τα Προϊόντα μας</h1>
<nav>{/* Navigation */}</nav>
</header>
);
}
// Αυτό το component απαιτεί αργά database queries
async function ProductList() {
// Αυτό μπορεί να πάρει 500ms+
const products = await db.products.findMany({
include: { reviews: true, pricing: true }
});
return (
<div className="product-grid">
{products.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
}
// Αυτό απαιτεί external API call
async function Recommendations() {
const recs = await fetch('https://ml-api.example.com/recommend', {
next: { revalidate: 300 }
}).then(r => r.json());
return <RecommendationCarousel items={recs} />;
}
// Η κύρια σελίδα με Suspense boundaries
export default function ProductsPage() {
return (
<main>
{/* Αυτό στέλνεται αμέσως */}
<PageHeader />
{/* Αυτό γίνεται stream μόλις είναι έτοιμο */}
<Suspense fallback={<ProductsSkeleton />}>
<ProductList />
</Suspense>
{/* Αυτό γίνεται stream ανεξάρτητα */}
<Suspense fallback={<RecommendationsSkeleton />}>
<Recommendations />
</Suspense>
</main>
);
}
Με αυτή τη δομή, ο browser λαμβάνει αμέσως το <header> και τα skeleton placeholders, και τα πραγματικά δεδομένα "ρέουν" μέσα στη σελίδα μόλις είναι έτοιμα.
Το αποτέλεσμα; TTFB κάτω από 100ms αντί για 500ms+ που θα χρειαζόταν χωρίς streaming.
Partial Hydration και Selective Hydration
Η Partial Hydration (ή Islands Architecture) πηγαίνει ένα βήμα παραπέρα: αντί να κάνει hydrate ολόκληρη τη σελίδα, κάνει hydrate μόνο τα interactive components. Frameworks όπως τα Astro και Qwik εφαρμόζουν αυτή τη στρατηγική -- και τα αποτελέσματα είναι εντυπωσιακά.
Η Selective Hydration του React 18+ επιτρέπει στα Suspense boundaries να γίνουν hydrated ανεξάρτητα, με προτεραιότητα στα components με τα οποία αλληλεπιδρά ο χρήστης. Πολύ πιο έξυπνο από το παλιό "all or nothing" approach.
Edge SSR
Ο συνδυασμός Streaming SSR με Edge Computing φέρνει τα καλύτερα και από τις δύο πλευρές: η σελίδα γίνεται render σε edge location κοντά στον χρήστη, μειώνοντας τόσο τον network latency όσο και τον processing time. Πλατφόρμες όπως Vercel Edge Functions, Cloudflare Workers και Deno Deploy υποστηρίζουν streaming SSR στο edge.
Resource Hints και Preloading
Τα Resource Hints επιτρέπουν στον browser να ξεκινήσει νωρίτερα τη φόρτωση κρίσιμων resources, μειώνοντας αποτελεσματικά τον αντίκτυπο του TTFB στη συνολική φόρτωση. Ας τα δούμε αναλυτικά.
Τύποι Resource Hints
<!-- dns-prefetch: Κάνε DNS lookup εκ των προτέρων -->
<!-- Χρήση: Domains τρίτων από τα οποία θα φορτωθούν resources αργότερα -->
<link rel="dns-prefetch" href="https://analytics.example.com">
<link rel="dns-prefetch" href="https://fonts.googleapis.com">
<!-- preconnect: DNS + TCP + TLS handshake εκ των προτέρων -->
<!-- Χρήση: Domains από τα οποία θα φορτωθούν κρίσιμα resources σύντομα -->
<link rel="preconnect" href="https://cdn.example.com" crossorigin>
<link rel="preconnect" href="https://api.example.com">
<!-- preload: Φόρτωσε αυτό το resource αμέσως (υψηλή προτεραιότητα) -->
<!-- Χρήση: Κρίσιμα resources που ο browser δεν θα ανακαλύψει νωρίς -->
<link rel="preload" href="/fonts/inter-var.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/css/critical.css" as="style">
<link rel="preload" href="/images/hero.webp" as="image" fetchpriority="high">
<!-- prefetch: Φόρτωσε αυτό στο background (χαμηλή προτεραιότητα) -->
<!-- Χρήση: Resources που θα χρειαστούν σε επόμενη πλοήγηση -->
<link rel="prefetch" href="/js/checkout.js">
<link rel="prefetch" href="/api/user/recommendations">
Πότε να Χρησιμοποιήσετε Κάθε Hint
dns-prefetch: Ελαφρύ, ελάχιστο κόστος. Χρησιμοποιήστε το ελεύθερα για third-party domainspreconnect: Πιο ακριβό (κρατάει ανοιχτή σύνδεση). Περιορίστε σε 2-4 origins για να μη σπαταλάτε resourcespreload: Υψηλή προτεραιότητα -- αν κάνετε preload πάρα πολλά resources, κανένα δεν ωφελείται. Εστιάστε στα 3-5 πιο κρίσιμα (LCP image, critical CSS, critical fonts)prefetch: Χαμηλή προτεραιότητα. Ιδανικό για resources που θα χρειαστούν στο αμέσως επόμενο βήμα του χρήστη
Σχέση με 103 Early Hints
Τα Resource Hints στο HTML και τα 103 Early Hints συμπληρώνουν το ένα το άλλο. Τα 103 Early Hints στέλνονται πριν καν φτάσει το HTML, δίνοντας στον browser ακόμα νωρίτερο head start.
Η βέλτιστη στρατηγική είναι:
- Χρησιμοποιήστε 103 Early Hints για τα πιο κρίσιμα resources (critical CSS, LCP image)
- Χρησιμοποιήστε
<link rel="preload">στο HTML ως fallback για browsers χωρίς υποστήριξη Early Hints - Χρησιμοποιήστε
<link rel="prefetch">για resources που θα χρειαστούν σε επόμενες πλοηγήσεις
Στρατηγικές Caching σε Πολλαπλά Επίπεδα
Η αποτελεσματική μείωση TTFB απαιτεί στρατηγική caching σε κάθε επίπεδο του stack.
Σκεφτείτε το caching σαν ομόκεντρους κύκλους: όσο πιο κοντά στον χρήστη γίνεται η εξυπηρέτηση, τόσο χαμηλότερο το TTFB. Απλό στη θεωρία, λίγο πιο περίπλοκο στην πράξη.
Τα Επίπεδα Caching
- Browser Cache: Τοπική αποθήκευση -- 0ms TTFB για cached resources
- Service Worker Cache: Προγραμματικός έλεγχος caching στο client-side
- CDN/Edge Cache: Cached responses κοντά γεωγραφικά στον χρήστη (<50ms TTFB)
- Application Cache: In-memory caching στον application server (Redis/Memcached)
- Database Cache: Query cache, materialized views, buffer pool
Stale-While-Revalidate Pattern
Η στρατηγική stale-while-revalidate είναι ιδιαίτερα ισχυρή: επιτρέπει στον browser (ή CDN) να σερβίρει cached content αμέσως, ενώ στο background κάνει revalidation.
Ο χρήστης πάντα λαμβάνει instant response. Το καλύτερο και από τους δύο κόσμους, αν θέλετε τη γνώμη μου.
# Ιδανικά Cache-Control patterns για διάφορα είδη content
# Στατικά assets με content hash στο filename
# π.χ. main.a1b2c3.js, styles.d4e5f6.css
Cache-Control: public, max-age=31536000, immutable
# HTML σελίδες - σερβίρουν cached αλλά revalidate στο background
Cache-Control: public, max-age=60, stale-while-revalidate=86400
# API responses - φρέσκα αλλά με σύντομο SWR window
Cache-Control: public, max-age=10, stale-while-revalidate=60, stale-if-error=86400
# Personalized content - μόνο private cache
Cache-Control: private, max-age=0, must-revalidate
# CDN-specific controls (ξεχωριστοί κανόνες για CDN vs browser)
Cache-Control: public, max-age=60
CDN-Cache-Control: max-age=3600, stale-while-revalidate=86400
Surrogate-Control: max-age=3600
Service Worker Caching Strategy
Ένα Service Worker μπορεί να εφαρμόσει προηγμένες στρατηγικές caching για ακόμη μηδενικό TTFB σε επαναλαμβανόμενες επισκέψεις:
// service-worker.js: Stale-While-Revalidate strategy
const CACHE_NAME = 'app-cache-v3';
// Εγκατάσταση: precache κρίσιμα resources
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll([
'/css/critical.css',
'/js/app.js',
'/fonts/inter-var.woff2',
'/offline.html'
]);
})
);
});
// Fetch: Stale-While-Revalidate για HTML pages
self.addEventListener('fetch', (event) => {
const { request } = event;
// Μόνο για GET requests navigation
if (request.mode === 'navigate') {
event.respondWith(staleWhileRevalidate(request));
return;
}
// Cache-first για στατικά assets
if (isStaticAsset(request.url)) {
event.respondWith(cacheFirst(request));
return;
}
});
async function staleWhileRevalidate(request) {
const cache = await caches.open(CACHE_NAME);
const cachedResponse = await cache.match(request);
// Ξεκίνα fetch στο background (revalidation)
const fetchPromise = fetch(request).then((networkResponse) => {
if (networkResponse.ok) {
// Ανανέωσε το cache με τη νέα απάντηση
cache.put(request, networkResponse.clone());
}
return networkResponse;
}).catch(() => {
// Αν αποτύχει το δίκτυο, επέστρεψε offline σελίδα
return cache.match('/offline.html');
});
// Επέστρεψε αμέσως το cached version (αν υπάρχει)
// ή περίμενε το network response
return cachedResponse || fetchPromise;
}
async function cacheFirst(request) {
const cached = await caches.match(request);
return cached || fetch(request);
}
function isStaticAsset(url) {
return /\.(js|css|woff2|png|jpg|webp|avif|svg)$/.test(url);
}
Μέτρηση και Παρακολούθηση
Η βελτιστοποίηση χωρίς μέτρηση είναι τυφλή εικασία. Τόσο απλά.
Χρειάζεστε τόσο synthetic testing (ελεγχόμενες συνθήκες, επαναληψιμότητα) όσο και Real User Monitoring (RUM) (πραγματικές συνθήκες χρηστών). Και τα δύο έχουν τον ρόλο τους.
Synthetic vs RUM
- Synthetic Testing (Lighthouse, WebPageTest): Ιδανικό για CI/CD pipelines, regression testing, σύγκριση πριν/μετά. Αλλά δεν αντικατοπτρίζει πάντα την πραγματική εμπειρία χρηστών.
- Real User Monitoring (CrUX, custom RUM): Τα πραγματικά δεδομένα. Δείχνει τι βιώνουν οι χρήστες σας σε ποικίλες συσκευές, δίκτυα και τοποθεσίες. Αυτό θέλετε.
Ολοκληρωμένο TTFB Monitoring με Navigation Timing API
// Ολοκληρωμένο RUM collection για TTFB
class TTFBMonitor {
constructor(endpoint) {
this.endpoint = endpoint;
this.init();
}
init() {
// Χρήση PerformanceObserver για πιο αξιόπιστα δεδομένα
if ('PerformanceObserver' in window) {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.entryType === 'navigation') {
this.processNavigationEntry(entry);
}
}
});
observer.observe({ type: 'navigation', buffered: true });
}
}
processNavigationEntry(entry) {
const metrics = {
// Βασικά TTFB metrics
ttfb: entry.responseStart - entry.requestStart,
// Ανάλυση σε επιμέρους στοιχεία
dns: entry.domainLookupEnd - entry.domainLookupStart,
tcp: entry.connectEnd - entry.connectStart,
tls: entry.requestStart - entry.secureConnectionStart,
serverProcessing: entry.responseStart - entry.requestStart,
contentDownload: entry.responseEnd - entry.responseStart,
// Πρωτόκολλο (h2, h3 κλπ.)
protocol: entry.nextHopProtocol,
// Τύπος σύνδεσης
connectionType: navigator.connection?.effectiveType || 'unknown',
// Πληροφορίες πλοήγησης
redirectCount: entry.redirectCount,
redirectTime: entry.redirectEnd - entry.redirectStart,
transferSize: entry.transferSize,
// Context
url: entry.name,
timestamp: Date.now(),
userAgent: navigator.userAgent
};
this.send(metrics);
}
send(metrics) {
// Χρήση sendBeacon για αξιόπιστη αποστολή
if (navigator.sendBeacon) {
navigator.sendBeacon(this.endpoint, JSON.stringify(metrics));
} else {
fetch(this.endpoint, {
method: 'POST',
body: JSON.stringify(metrics),
keepalive: true
});
}
}
}
// Ενεργοποίηση
const monitor = new TTFBMonitor('/api/rum/collect');
Server-Timing Header για Debugging
Ο Server-Timing header σας επιτρέπει να εκθέσετε πληροφορίες χρονισμού από τον server απευθείας στο DevTools του browser. Πολύτιμο εργαλείο που πολλοί παραβλέπουν:
// Express middleware για Server-Timing header
function serverTimingMiddleware(req, res, next) {
const timings = [];
const startTime = process.hrtime.bigint();
// Μέτρηση database time
const originalQuery = req.app.locals.db.query;
let dbTime = 0n;
req.app.locals.db.query = async (...args) => {
const dbStart = process.hrtime.bigint();
const result = await originalQuery.apply(req.app.locals.db, args);
dbTime += process.hrtime.bigint() - dbStart;
return result;
};
// Μέτρηση cache time
const originalCacheGet = req.app.locals.cache.get;
let cacheTime = 0n;
let cacheHit = false;
req.app.locals.cache.get = async (...args) => {
const cacheStart = process.hrtime.bigint();
const result = await originalCacheGet.apply(req.app.locals.cache, args);
cacheTime += process.hrtime.bigint() - cacheStart;
if (result) cacheHit = true;
return result;
};
// Πριν σταλεί η απάντηση, πρόσθεσε τα timings
const originalEnd = res.end;
res.end = function(...args) {
const totalTime = Number(process.hrtime.bigint() - startTime) / 1e6;
const dbMs = Number(dbTime) / 1e6;
const cacheMs = Number(cacheTime) / 1e6;
res.setHeader('Server-Timing', [
`total;dur=${totalTime.toFixed(1)};desc="Total"`,
`db;dur=${dbMs.toFixed(1)};desc="Database"`,
`cache;dur=${cacheMs.toFixed(1)};desc="Cache ${cacheHit ? 'HIT' : 'MISS'}"`,
`render;dur=${(totalTime - dbMs - cacheMs).toFixed(1)};desc="Render"`
].join(', '));
originalEnd.apply(res, args);
};
next();
}
app.use(serverTimingMiddleware);
Αυτά τα timings εμφανίζονται στο Chrome DevTools > Network > Timing tab, δίνοντάς σας ακριβή εικόνα του τι κάνει ο server κατά τη διάρκεια του processing.
Αξιοποίηση Server-Timing στο Client
// Ανάγνωση Server-Timing metrics στον client
const [navigation] = performance.getEntriesByType('navigation');
if (navigation && navigation.serverTiming) {
navigation.serverTiming.forEach(timing => {
console.log(`${timing.name}: ${timing.duration}ms (${timing.description})`);
// Παράδειγμα εξόδου:
// total: 145.3ms (Total)
// db: 82.1ms (Database)
// cache: 2.4ms (Cache MISS)
// render: 60.8ms (Render)
});
}
Checklist Βελτιστοποίησης TTFB
Ολοκληρώνουμε με ένα πρακτικό checklist που καλύπτει όλες τις τεχνικές που αναλύσαμε. Κρατήστε το κοντά -- θα σας φανεί χρήσιμο.
Server-Side Optimization
- Βελτιστοποίηση database queries -- indexes, αποφυγή N+1, query analysis
- Υλοποίηση in-memory caching με Redis ή Memcached
- Σωστή ρύθμιση connection pooling για database και upstream services
- Ενεργοποίηση HTTP Keep-Alive τόσο στον reverse proxy όσο και στο backend
- Χρήση async/non-blocking I/O operations
- Σωστή ρύθμιση HTTP caching headers (
Cache-Control,ETag,Last-Modified)
CDN και Edge
- Χρήση CDN με PoPs κοντά στο κοινό-στόχο σας
- Αξιοποίηση edge compute (Cloudflare Workers, Lambda@Edge) για δυναμικό content
- Σωστή ρύθμιση CDN cache TTLs και purge strategies
- Χρήση
stale-while-revalidateστο CDN layer - Αξιοποίηση edge databases για data-driven edge rendering
Πρωτόκολλα και Σύνδεση
- Ενεργοποίηση HTTP/3 με
Alt-Svcheader - Σωστή ρύθμιση TLS 1.3 με 0-RTT support
- Ενεργοποίηση OCSP stapling για ταχύτερη TLS verification
- Χρήση αξιόπιστου DNS provider με χαμηλά resolution times
- Υλοποίηση 103 Early Hints (με προσοχή στην επίδραση σε mobile)
Rendering και Frontend
- Υλοποίηση Streaming SSR με Suspense boundaries
- Αξιοποίηση React Server Components ή αντίστοιχα framework features
- Εξέταση Partial Hydration / Islands Architecture όπου εφικτό
- Χρήση resource hints:
preconnect,dns-prefetch,preload - Χρήση
fetchpriority="high"στο LCP resource
Caching Πολλαπλών Επιπέδων
- Σωστοί
Cache-Controlheaders ανά τύπο content - Υλοποίηση Service Worker με stale-while-revalidate strategy
- Αξιοποίηση browser cache μέσω content hashing στα filenames
- Application-level caching με κατάλληλα TTLs
- Database query caching και materialized views
Μέτρηση και Monitoring
- Εγκατάσταση RUM monitoring για TTFB σε πραγματικούς χρήστες
- Χρήση
Server-Timingheaders για debugging server processing - Ρύθμιση alerts για TTFB regression (p75 > 800ms)
- Synthetic monitoring σε CI/CD pipeline
- Ανάλυση TTFB ανά γεωγραφική τοποθεσία, τύπο σύνδεσης και πρωτόκολλο
Τελική Σκέψη
Η βελτιστοποίηση του TTFB δεν είναι μια μεμονωμένη ενέργεια -- είναι μια συνεχής διαδικασία. Τα δεδομένα RUM σας πρέπει να καθοδηγούν τις αποφάσεις σας.
Ξεκινήστε μετρώντας, εντοπίστε τα μεγαλύτερα bottlenecks, εφαρμόστε τις κατάλληλες βελτιστοποιήσεις, και μετρήστε ξανά. Repeat.
Με τη σωστή στρατηγική, TTFB κάτω από 200ms στο p75 είναι απολύτως εφικτό για τις περισσότερες ιστοσελίδες -- πολύ κάτω από το όριο των 800ms της Google, και αρκετά χαμηλό ώστε να μη γίνεται bottleneck για κανένα Core Web Vital.
Θυμηθείτε: κάθε millisecond TTFB που εξοικονομείτε δίνει στον browser περισσότερο χρόνο για parsing, rendering και αλληλεπίδραση. Σε τελική ανάλυση, αυτός είναι ο στόχος -- ταχύτερες, πιο responsive ιστοσελίδες για τους χρήστες σας, ανεξάρτητα από τη συσκευή ή τη σύνδεσή τους.