Αυτός ο οδηγός περιγράφει βασικούς κανόνες ασφαλείας Nginx για servers production φιλοξενίας.
Στόχος είναι να μειωθούν:
- η πρόσβαση σε ευαίσθητα αρχεία
- η εκτέλεση PHP μέσα σε φακέλους uploads
- η έκθεση κρυφών αρχείων (dotfiles)
- η κατάχρηση του XML-RPC
- το directory probing (σάρωση φακέλων)
- η υπερφόρτωση του server από κακόβουλες αιτήσεις (rate limiting)
- η έκθεση κρυπτογράφησης μέσω αδύναμων TLS ρυθμίσεων
- η διαρροή μυστικών μέσω συμπίεσης gzip (BREACH attack)
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}Τι κάνει κάθε γραμμή:
location ~ /\.→ Ταιριάζει με οποιοδήποτε URI που περιέχει τη σειρά χαρακτήρων/.(κάθετη γραμμή αμέσως ακολουθούμενη από τελεία), δηλαδή κρυφά αρχεία ή φακέλους όπως/.env,/.git,/.htaccess. Το~σημαίνει ότι χρησιμοποιείται κανονική έκφραση (regex), με διάκριση πεζών-κεφαλαίων.deny all;→ Απαγορεύει την πρόσβαση σε όλους τους επισκέπτες, επιστρέφοντας HTTP 403 Forbidden.access_log off;→ Δεν καταγράφει αυτές τις αιτήσεις στο access log, ώστε να μη γεμίζουν τα logs από bot scans.log_not_found off;→ Δεν καταγράφει τα 404 errors για αρχεία που δεν βρέθηκαν, μειώνοντας τον θόρυβο στα logs.
Προστατεύει από:
.env→ Αρχείο με credentials και μυστικά κλειδιά της εφαρμογής.git→ Ολόκληρο το git repository με τον source code.htaccess→ Αρχεία ρυθμίσεων Apache που μπορεί να αποκαλύψουν πληροφορίες- Οποιοδήποτε άλλο κρυφό αρχείο που δεν πρέπει να είναι δημόσια προσβάσιμο
location = /wp-config.php {
deny all;
}
location = /readme.html {
deny all;
}
location = /license.txt {
deny all;
}Τι κάνει κάθε γραμμή:
location = /wp-config.php→ Το=σημαίνει ακριβής αντιστοίχιση (exact match) με το συγκεκριμένο URL. Ταιριάζει μόνο στο/wp-config.phpκαι τίποτα άλλο.location = /readme.html→ Αποκλείει το αρχείο που αποκαλύπτει την έκδοση του WordPress που χρησιμοποιείται.location = /license.txt→ Αποκλείει το αρχείο άδειας που επίσης αποκαλύπτει την έκδοση του WordPress.deny all;→ Σε κάθε block, απαγορεύει πλήρως την πρόσβαση.
Γιατί είναι σημαντικό:
- Το
wp-config.phpπεριέχει credentials βάσης δεδομένων, κλειδιά κρυπτογράφησης και ρυθμίσεις ασφαλείας — είναι το πιο κρίσιμο αρχείο ενός WordPress site. - Το
readme.htmlκαιlicense.txtαποκαλύπτουν την έκδοση WordPress, βοηθώντας τους επιτιθέμενους να εντοπίσουν γνωστές ευπάθειες.
location ~* /(?:uploads|files)/.*\.php$ {
deny all;
}Τι κάνει κάθε γραμμή:
location ~* /(?:uploads|files)/.*\.php$→ Ταιριάζει με οποιοδήποτε URI που περιέχει τον φάκελο/uploads/ή/files/και τελειώνει με.php. Το~*σημαίνει regex χωρίς διάκριση πεζών-κεφαλαίων (case-insensitive). Το(?:...)είναι μη-καταγραφική ομάδα regex. Το.*ταιριάζει με οποιουσδήποτε χαρακτήρες, συμπεριλαμβανομένων υποφακέλων και του ονόματος αρχείου (π.χ./uploads/2024/01/shell.php).deny all;→ Αποκλείει οποιαδήποτε απόπειρα πρόσβασης σε PHP αρχείο μέσα στους φακέλους uploads.
Γιατί είναι κρίσιμο:
Αυτό αποτρέπει επιθέσεις τύπου Remote Code Execution (RCE). Αν ένας εισβολέας καταφέρει να ανεβάσει ένα κακόβουλο PHP αρχείο (web shell) μέσω της φόρμας αποστολής αρχείων, αυτός ο κανόνας το εμποδίζει να εκτελεστεί. Είναι βασικό hardening για κάθε WordPress installation.
location = /xmlrpc.php {
deny all;
}Τι κάνει:
location = /xmlrpc.php→ Ακριβής αντιστοίχιση με το αρχείοxmlrpc.phpπου υλοποιεί το πρωτόκολλο XML-RPC στο WordPress.deny all;→ Αποκλείει πλήρως κάθε αίτηση προς αυτό το αρχείο.
Γιατί απενεργοποιείται:
Το XML-RPC χρησιμοποιείται παλαιότερα για απομακρυσμένη διαχείριση του WordPress. Σήμερα αποτελεί κύριο στόχο για:
- Brute force επιθέσεις με χιλιάδες δοκιμές κωδικών σε μία μόνο αίτηση (λόγω του
system.multicall) - DDoS amplification attacks
- Botnet exploitation
Αν το site δεν χρειάζεται XML-RPC (π.χ. δεν χρησιμοποιεί Jetpack ή mobile app διαχείρισης), το κλείνουμε εντελώς.
location ^~ /vendor/ {
deny all;
return 403;
}Τι κάνει κάθε γραμμή:
location ^~ /vendor/→ Ταιριάζει με οποιοδήποτε URL που ξεκινά με/vendor/. Το^~σημαίνει prefix match με υψηλή προτεραιότητα — αν ταιριάσει, το Nginx δεν ελέγχει άλλα regex location blocks.deny all;→ Απαγορεύει την πρόσβαση.return 403;→ Επιστρέφει ρητά HTTP 403 Forbidden στον επισκέπτη (διπλή ασφάλεια).
Χρήσιμο για:
- Composer-based apps → Ο φάκελος
vendor/περιέχει εκατοντάδες βιβλιοθήκες με εκτελέσιμο κώδικα που δεν πρέπει να είναι δημόσια προσβάσιμες - WHMCS → Προστασία φακέλων με ιδιόκτητο κώδικα και ρυθμίσεις
- Laravel και άλλα PHP frameworks → Αποτροπή απευθείας πρόσβασης σε αρχεία εκτός του
public/directory
location ~* \.(bak|conf|dist|fla|inc|ini|log|psd|sh|sql|swp|tmp)$ {
deny all;
}Τι κάνει κάθε γραμμή:
location ~* \.(bak|conf|dist|fla|inc|ini|log|psd|sh|sql|swp|tmp)$→ Ταιριάζει case-insensitive με οποιοδήποτε URI που τελειώνει με μία από τις παρακάτω καταλήξεις αρχείων. Το\.escapes την τελεία ώστε να αντιπροσωπεύει κυριολεκτικά τελεία και όχι οποιονδήποτε χαρακτήρα. Το$σημαίνει τέλος του URI (του path της αίτησης).deny all;→ Αποκλείει πρόσβαση σε όλα αυτά τα αρχεία.
Τύποι αρχείων που προστατεύονται:
| Κατάληξη | Τι είναι |
|---|---|
.bak |
Αρχεία backup που μπορεί να περιέχουν παλιές ρυθμίσεις ή credentials |
.conf |
Αρχεία ρυθμίσεων με ευαίσθητες παραμέτρους |
.dist |
Αρχεία template ρυθμίσεων |
.fla |
Adobe Flash source files |
.inc |
PHP include files με κώδικα ή ρυθμίσεις |
.ini |
Αρχεία ρυθμίσεων (π.χ. php.ini overrides) |
.log |
Log αρχεία με ευαίσθητες πληροφορίες |
.psd |
Adobe Photoshop αρχεία (source files σχεδιασμού) |
.sh |
Shell scripts με εντολές συστήματος |
.sql |
Dump βάσεων δεδομένων με ολόκληρο το περιεχόμενο |
.swp |
Vim swap files που μπορεί να αποκαλύψουν source code |
.tmp |
Προσωρινά αρχεία με απρόβλεπτο περιεχόμενο |
location /wp-admin {
allow YOUR_IP;
deny all;
try_files $uri $uri/ /index.php?$args;
}Τι κάνει κάθε γραμμή:
location /wp-admin→ Ταιριάζει με οποιοδήποτε URL που ξεκινά με/wp-admin(prefix match).allow YOUR_IP;→ Επιτρέπει πρόσβαση μόνο από τη συγκεκριμένη IP διεύθυνση. Αντικατέστησε τοYOUR_IPμε την πραγματική σου IP (π.χ.allow 203.0.113.42;). Μπορείς να προσθέσεις πολλαπλές γραμμέςallowγια πολλαπλές IPs.deny all;→ Αποκλείει όλους τους υπόλοιπους. Οι κανόνεςallow/denyεκτελούνται με σειρά — πρώτα ελέγχεται τοallow, μετά τοdeny.try_files $uri $uri/ /index.php?$args;→ Για τους επιτρεπόμενους χρήστες, δοκιμάζει να σερβίρει το αρχείο απευθείας, μετά ως φάκελο, και τέλος το προωθεί στοindex.phpγια επεξεργασία από το WordPress.
Προσοχή: Αυτό είναι χρήσιμο μόνο αν έχεις σταθερή (static) IP ή VPN με static IP. Με dynamic IP θα κλειδωθείς εκτός του admin panel.
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;Τι κάνει κάθε γραμμή:
add_header X-Frame-Options "SAMEORIGIN" always;→ Αποτρέπει το site από το να φορτωθεί μέσα σε<iframe>από άλλα domains. Προστατεύει από Clickjacking επιθέσεις. ΤοSAMEORIGINεπιτρέπει iframes μόνο από το ίδιο domain.add_header X-Content-Type-Options "nosniff" always;→ Αποτρέπει τον browser από το να μαντεύει τον τύπο MIME ενός αρχείου. Προστατεύει από επιθέσεις MIME sniffing όπου ένα αρχείο εκτελείται με λάθος τύπο.add_header Referrer-Policy "strict-origin-when-cross-origin" always;→ Ελέγχει ποιες πληροφορίες Referer αποστέλλονται. Μεstrict-origin-when-cross-origin, αποστέλλεται μόνο το origin (domain) σε cross-origin αιτήσεις, χωρίς το πλήρες URL. Προστατεύει από διαρροή ευαίσθητων URLs.add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;→ Απενεργοποιεί συγκεκριμένες δυνατότητες browser. Η κενή παρένθεση()σημαίνει ότι η δυνατότητα είναι απενεργοποιημένη για όλα τα origins. Αποτρέπει κακόβουλο JavaScript από το να αποκτήσει πρόσβαση σε κάμερα, μικρόφωνο ή τοποθεσία.always→ Οι headers αποστέλλονται σε όλες τις απαντήσεις, συμπεριλαμβανομένων των 4xx και 5xx σφαλμάτων (όχι μόνο σε 200 OK).
Σημείωση για Content-Security-Policy:
Το Content-Security-Policy (CSP) δεν προστίθεται τυφλά. Χρειάζεται προσεκτικό testing ανά site γιατί μπορεί να σπάσει JavaScript, CSS ή fonts από third-party CDNs.
server_tokens off;Τι κάνει:
server_tokens off;→ Απενεργοποιεί την εμφάνιση της έκδοσης του Nginx στις HTTP απαντήσεις. Συγκεκριμένα, αφαιρεί την έκδοση από:- Την επικεφαλίδα
Server:(π.χ. απόnginx/1.24.0γίνεται απλώςnginx) - Τις σελίδες σφαλμάτων (404, 403, 500 κ.λπ.) όπου εμφανιζόταν η έκδοση στο footer
- Την επικεφαλίδα
Γιατί είναι σημαντικό:
Αποκρύπτει την ακριβή έκδοση του Nginx, δυσκολεύοντας τους επιτιθέμενους να εντοπίσουν γνωστές ευπάθειες (CVEs) για την έκδοση που χρησιμοποιείς. Εφαρμόζεται στο κεντρικό αρχείο ρυθμίσεων nginx.conf.
Το Rate Limiting περιορίζει πόσες αιτήσεις μπορεί να στείλει μία IP σε δεδομένο χρόνο. Ορίζεται σε δύο βήματα: πρώτα δηλώνεται η ζώνη (zone) στο http block του nginx.conf, και μετά εφαρμόζεται σε συγκεκριμένα location blocks στο site configuration.
Βήμα 1 — Ορισμός ζωνών στο nginx.conf (http block):
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
limit_req_zone $binary_remote_addr zone=general:10m rate=30r/s;Τι κάνει κάθε γραμμή:
-
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;$binary_remote_addr→ Χρησιμοποιεί τη δυαδική μορφή της IP του επισκέπτη ως κλειδί — πιο αποδοτικό από τη string μορφή$remote_addr.zone=login:10m→ Δημιουργεί κοινόχρηστη μνήμη με όνομαloginκαι μέγεθος 10MB (χωρά περίπου 160.000 IPs).rate=5r/m→ Επιτρέπει μέγιστο 5 αιτήσεις ανά λεπτό από κάθε IP για αυτή τη ζώνη.
-
limit_req_zone $binary_remote_addr zone=general:10m rate=30r/s;- Παρόμοια λογική, αλλά με
rate=30r/s→ 30 αιτήσεις ανά δευτερόλεπτο για γενική χρήση.
- Παρόμοια λογική, αλλά με
Βήμα 2 — Εφαρμογή στο site configuration:
# Προστασία login page
location = /wp-login.php {
limit_req zone=login burst=3 nodelay;
try_files $uri $uri/ /index.php?$args;
}
# Γενικό rate limiting για όλο το site
location / {
limit_req zone=general burst=50 nodelay;
try_files $uri $uri/ /index.php?$args;
}Τι κάνει κάθε γραμμή:
-
limit_req zone=login burst=3 nodelay;zone=login→ Χρησιμοποιεί τη ζώνηloginπου ορίσαμε παραπάνω.burst=3→ Επιτρέπει έως 3 επιπλέον αιτήσεις σε ουρά αναμονής πάνω από το όριο (για να μην αποκλείονται νόμιμοι χρήστες που κάνουν refresh).nodelay→ Οι αιτήσεις του burst εξυπηρετούνται αμέσως χωρίς καθυστέρηση· υπέρβαση του burst επιστρέφει HTTP 503.
-
limit_req zone=general burst=50 nodelay;→ Ίδια λογική για γενική κυκλοφορία, με μεγαλύτερο burst για να δεχθεί νόμιμες ομαδικές αιτήσεις (π.χ. φόρτωση πολλών assets).
Γιατί είναι σημαντικό:
Το rate limiting αποτρέπει:
- Brute force επιθέσεις στο
/wp-login.php(χιλιάδες δοκιμές κωδικών) - Credential stuffing (αυτόματη δοκιμή διαρρευσάντων κωδικών)
- HTTP flood DDoS που εξαντλεί τους πόρους του server
- Scraping και automated scanning του περιεχομένου
Οι παρακάτω ρυθμίσεις εφαρμόζονται στο server block του HTTPS site configuration και ενισχύουν την κρυπτογράφηση.
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256';
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;Τι κάνει κάθε γραμμή:
-
ssl_protocols TLSv1.2 TLSv1.3;→ Ενεργοποιεί μόνο TLS 1.2 και TLS 1.3. Απενεργοποιεί τα παλαιά και ανασφαλή SSLv3, TLS 1.0 και TLS 1.1 που έχουν γνωστές ευπάθειες (POODLE, BEAST, SWEET32). -
ssl_ciphers '...';→ Ορίζει τη λίστα επιτρεπόμενων αλγορίθμων κρυπτογράφησης. Όλοι ξεκινούν μεECDHE-ήDHE-που σημαίνει Perfect Forward Secrecy (PFS) — αν κλαπεί το private key του server, οι προηγούμενες συνομιλίες παραμένουν προστατευμένες. Χρησιμοποιείται η λίστα Mozilla Modern / Intermediate. -
ssl_prefer_server_ciphers off;→ Αφήνει τον client να επιλέξει τον αλγόριθμο από τη λίστα (ιδανικό για TLS 1.3 που δεν υποστηρίζει αυτή την επιλογή). -
ssl_session_cache shared:SSL:10m;→ Δημιουργεί κοινόχρηστη cache για SSL sessions μεγέθους 10MB μεταξύ των worker processes, επιτρέποντας επαναχρησιμοποίηση sessions (TLS session resumption) χωρίς πλήρη handshake. -
ssl_session_timeout 1d;→ Τα αποθηκευμένα session parameters λήγουν μετά από 1 μέρα. Μειώνει τον αριθμό των πλήρων TLS handshakes βελτιώνοντας την απόδοση. -
ssl_session_tickets off;→ Απενεργοποιεί τα TLS session tickets για λόγους ασφαλείας — τα session tickets κρυπτογραφούνται με ένα κλειδί του server που αν διαρρεύσει, εκθέτει όλες τις προηγούμενες sessions (σπάει το Perfect Forward Secrecy). -
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;→ Ενεργοποιεί HSTS (HTTP Strict Transport Security):max-age=63072000→ Ο browser θυμάται για 2 χρόνια (σε δευτερόλεπτα) ότι αυτό το domain επιτρέπει μόνο HTTPS.includeSubDomains→ Εφαρμόζεται και σε όλα τα subdomains.preload→ Επιτρέπει ένταξη στη HSTS preload list των browsers ώστε το domain φορτώνεται πάντα με HTTPS, ακόμα και την πρώτη φορά.
Γιατί είναι σημαντικό:
Αποτρέπει επιθέσεις:
- Downgrade attacks (POODLE, BEAST) — αναγκάζοντας σε παλαιά πρωτόκολλα
- SSL stripping — υποκλοπή σύνδεσης πριν γίνει HTTPS (αντιμετωπίζεται από HSTS)
- Man-in-the-Middle (MitM) — παρεμβολή μεταξύ client και server
- Cipher suite downgrade — εξαναγκασμός σε αδύναμο αλγόριθμο κρυπτογράφησης
Η συμπίεση gzip βελτιώνει την απόδοση, αλλά μπορεί να χρησιμοποιηθεί σε επιθέσεις τύπου BREACH για να εξαχθούν μυστικά (όπως CSRF tokens) από κρυπτογραφημένες HTTPS αποκρίσεις.
Πώς λειτουργεί η επίθεση BREACH: Αν ένα HTTP response (α) συμπιέζεται με gzip, (β) περιέχει ένα μυστικό (π.χ. CSRF token) και (γ) αντανακλά είσοδο του χρήστη, τότε ένας επιτιθέμενος μπορεί να μετρήσει πόσο μεταβάλλεται το μέγεθος της απόκρισης για να μαντέψει το μυστικό χαρακτήρα-χαρακτήρα.
⚠️ Σημαντική τεχνική λεπτομέρεια: Το Nginx συμπιέζει πάντα ταtext/htmlresponses όταν τοgzip onείναι ενεργό, ανεξάρτητα από το τι ορίζεται στοgzip_types. Η οδηγίαgzip_typesπροσθέτει μόνο επιπλέον τύπους πέρα από τοtext/html— δεν μπορεί να τον εξαιρέσει. Επομένως, για να αποτρέψεις τη συμπίεση HTML, πρέπει να επιλέξεις μία από τις παρακάτω στρατηγικές.
Επιλογή Α — Πλήρης απενεργοποίηση gzip (μέγιστη ασφάλεια):
gzip off;Η πιο απλή και ασφαλής προσέγγιση. Κατάλληλη για sites που χειρίζονται ιδιαίτερα ευαίσθητα δεδομένα ή για περιβάλλοντα όπου το CDN (π.χ. Cloudflare) αναλαμβάνει ήδη τη συμπίεση.
Επιλογή Β — Gzip μόνο για static assets μέσω ξεχωριστού location block:
# Gzip ενεργό μόνο για συγκεκριμένα στατικά αρχεία
location ~* \.(css|js|svg|woff2|woff)$ {
gzip_static on;
expires max;
add_header Cache-Control "public";
}
# Απενεργοποίηση gzip για HTML (δυναμικές σελίδες με CSRF tokens)
gzip off;Τι κάνει κάθε γραμμή:
gzip off;→ Απενεργοποιεί τη δυναμική συμπίεση gzip για όλες τις αποκρίσεις — το κύριο μέτρο κατά BREACH.gzip_static on;→ Σερβίρει προ-συμπιεσμένα αρχεία.gzαν υπάρχουν δίπλα στο αρχικό (π.χ.style.css.gz). Δεν εκτελεί δυναμική συμπίεση κατά τη διάρκεια της αίτησης — άρα δεν εκτίθεται σε BREACH.expires max;→ Ορίζει μέγιστο χρόνο caching για στατικά αρχεία.add_header Cache-Control "public";→ Επιτρέπει στα CDN και browser caches να αποθηκεύουν αυτά τα αρχεία.
Γιατί είναι σημαντικό:
| Προσέγγιση | Ασφάλεια BREACH | Απόδοση |
|---|---|---|
gzip off |
✅ Πλήρης προστασία | |
gzip_static για assets |
✅ Προστατευμένο (χωρίς δυναμική συμπίεση) | ✅ Καλή απόδοση για CSS/JS |
gzip on χωρίς μέτρα |
❌ Εκτεθειμένο | ✅ Καλύτερη απόδοση |
Εναλλακτικά μέτρα (application level): Αν δεν μπορείς να απενεργοποιήσεις το gzip, η εφαρμογή μπορεί να προστατευθεί με CSRF token masking (τυχαία μεταβολή της αναπαράστασης του token σε κάθε αίτηση) — αυτή είναι η προσέγγιση που χρησιμοποιεί το Django και άλλα frameworks.
Όλοι οι παραπάνω κανόνες σε ένα ενιαίο block, έτοιμο για χρήση στο site configuration:
# --- nginx.conf (http block) ---
# Ορισμός ζωνών rate limiting
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
limit_req_zone $binary_remote_addr zone=general:10m rate=30r/s;
# Απόκρυψη έκδοσης Nginx
server_tokens off;
# Gzip — απενεργοποίηση για προστασία BREACH (HTML συμπιέζεται πάντα από το Nginx)
# Χρησιμοποίησε gzip_static για προ-συμπιεσμένα στατικά αρχεία
gzip off;
# --- /etc/nginx/sites-available/example.com (server block) ---
# SSL/TLS Hardening
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256';
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# Επικεφαλίδες ασφαλείας
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 Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
# Αποκλεισμός κρυφών αρχείων (.env, .git, .htaccess κ.λπ.)
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# Αποκλεισμός ευαίσθητων WordPress αρχείων
location = /wp-config.php { deny all; }
location = /readme.html { deny all; }
location = /license.txt { deny all; }
# Αποκλεισμός εκτέλεσης PHP στα uploads (αποτροπή web shells)
location ~* /(?:uploads|files)/.*\.php$ {
deny all;
}
# Απενεργοποίηση XML-RPC (αποτροπή brute force και DDoS)
location = /xmlrpc.php {
deny all;
}
# Αποκλεισμός αρχείων backup, ρυθμίσεων και logs
location ~* \.(bak|conf|dist|fla|inc|ini|log|psd|sh|sql|swp|tmp)$ {
deny all;
}
# Προστασία login page με rate limiting
location = /wp-login.php {
limit_req zone=login burst=3 nodelay;
try_files $uri $uri/ /index.php?$args;
}
# Γενικό rate limiting για όλο το site
location / {
limit_req zone=general burst=50 nodelay;
try_files $uri $uri/ /index.php?$args;
}- Τα rules αυτά εφαρμόζονται συνήθως ανά site template (π.χ.
/etc/nginx/sites-available/example.com) - Σε WooCommerce, WHMCS και Laravel χρειάζεται προσαρμογή ανά εφαρμογή
- Μη βάζεις CSP (Content-Security-Policy) χωρίς πρώτα testing στο συγκεκριμένο site
- Τα Nginx rules συμπληρώνουν — δεν αντικαθιστούν — firewall και Fail2Ban
- Μετά από κάθε αλλαγή, δοκίμασε τη σύνταξη με
nginx -tκαι κάνε reload μεsystemctl reload nginx - Πριν ενεργοποιήσεις HSTS, βεβαιώσου ότι το SSL certificate είναι έγκυρο — αν λήξει, οι χρήστες δεν θα μπορούν να αποκτήσουν πρόσβαση στο site μέχρι να ανανεωθεί
Cloudflare / WAF (Web Application Firewall)
↓
Κανόνες Ασφαλείας Nginx
↓
Rate Limiting (Περιορισμός ρυθμού αιτήσεων)
↓
Fail2Ban (Αυτόματο μπλοκάρισμα κακόβουλων IPs)
↓
Εφαρμογή (WordPress / Laravel / WHMCS κ.λπ.)