8. Hosting a Static Website and Changing the Configuration
8.9 Basic Hardening of a Static Web Server
The site is live – now let's make it a little more secure. These changes are small, but scanners such as Nikto spot them immediately.
साइट चालू झाली – आता ती थोडी सिक्युअर करूया. हे बदल छोटे आहेत, पण Nikto सारखे स्कॅनर्स ते लगेच पकडतात.
साइट चालू हो गई – अब इसे थोड़ा सिक्योर करते हैं. ये बदलाव छोटे हैं, लेकिन Nikto जैसे स्कैनर इन्हें तुरंत पकड़ लेते हैं.
# Nginx – in the http { } block of nginx.conf
server_tokens off; # hide version number
# in each server block
autoindex off;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
location ~ /\.(?!well-known) { deny all; } # block .git, .env, .htaccess
# Apache – /etc/httpd/conf.d/security.conf (Ubuntu: conf-available/security.conf)
ServerTokens Prod
ServerSignature Off
TraceEnable Off
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
<DirectoryMatch "/\.(?!well-known)">
Require all denied
</DirectoryMatch>
curl -I http://localhost # check: no version in the Server header, new headers present
On Ubuntu Apache, enable the headers module first with sudo a2enmod headers.
Why this matters for security
Hiding versions does not fix vulnerabilities, but it removes free information. Blocking dot-files stops leaks of .git/ (entire source code) and .env (passwords) – both are among the first things web scanners request.
In simple words (sopya bhashet)
Friends, hardening is like putting locks on a house. A running site is like an open front door; now we close the windows – we hide the version number, switch off the folder file list (directory listing), and put a "No entry" board on hidden files such as .git and .env. This does not stop every attack, but it greatly reduces the free information an attacker gets. And after every change, run nginx -t or apachectl configtest – never skip this.
मित्रांनो, हार्डनिंग म्हणजे घराला कुलूप लावण्यासारखं आहे. साइट चालू आहे म्हणजे दरवाजा उघडा आहे; आता आपण खिडक्या बंद करतो – व्हर्जन नंबर लपवतो, फोल्डरमधल्या फाइल्सची लिस्ट (directory listing) बंद करतो, आणि .git, .env सारख्या हिडन फाइल्सवर "नो एंट्री" चा बोर्ड लावतो. यामुळे प्रत्येक अटॅक थांबत नाही, पण अटॅकरला मिळणारी फुकट माहिती खूप कमी होते. आणि प्रत्येक बदलानंतर nginx -t किंवा apachectl configtest रन करा – हे कधीच विसरू नका.
दोस्तों, हार्डनिंग मतलब घर पर ताला लगाने जैसा है. साइट चालू है मतलब दरवाज़ा खुला है; अब हम खिड़कियाँ बंद करते हैं – वर्ज़न नंबर छुपाते हैं, फ़ोल्डर की फ़ाइलों की लिस्ट (directory listing) बंद करते हैं, और .git, .env जैसी हिडन फ़ाइलों पर "नो एंट्री" का बोर्ड लगाते हैं. इससे हर अटैक नहीं रुकता, लेकिन अटैकर को मिलने वाली मुफ़्त जानकारी बहुत कम हो जाती है. और हर बदलाव के बाद nginx -t या apachectl configtest रन करो – यह कभी मत भूलना.
Step by step on Amazon Linux 2023 (Nginx)
We harden the mysite site from 8.2 (/etc/nginx/conf.d/mysite.conf, server_name = your public IP).
# 0. keep a copy of both files before you edit
sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak
sudo cp /etc/nginx/conf.d/mysite.conf /etc/nginx/conf.d/mysite.conf.bak
# 1. note the headers BEFORE (Host header = public IP, so our server block answers)
IP=$(curl -s https://checkip.amazonaws.com)
curl -I http://localhost -H "Host: $IP"
# 2. hide the version: add server_tokens off; inside the http { } block
sudo vi /etc/nginx/nginx.conf
# 3. in the server { } block of mysite.conf add: autoindex off; the three add_header lines;
# and the location block that denies dot-files (see the Nginx block above)
sudo vi /etc/nginx/conf.d/mysite.conf
# 4. test the syntax, then reload only if the test passes
sudo nginx -t && sudo service nginx reload
# 5. check AFTER
curl -I http://localhost -H "Host: $IP"
For Apache on Amazon Linux the flow is the same: sudo yum install -y httpd (if not installed), put the Apache lines above in /etc/httpd/conf.d/security.conf, then sudo apachectl configtest && sudo service httpd reload.
Worked example 1 – Mauli Dairy, Pune (fictional): before and after headers
A fictional Pune dairy hosts its price-list page on an EC2 instance with Nginx. Step 1 and step 5 above give:
| Before hardening | After hardening | |
|---|---|---|
| Status line | HTTP/1.1 200 OK |
HTTP/1.1 200 OK |
| Server | nginx/<version number> |
nginx (no version) |
| X-Content-Type-Options | – | nosniff |
| X-Frame-Options | – | SAMEORIGIN |
| Referrer-Policy | – | strict-origin-when-cross-origin |
The page itself did not change for customers. What changed: a scanner no longer learns the exact Nginx version, the browser will not guess file types (nosniff), and other websites cannot load the dairy's page inside a hidden frame (SAMEORIGIN), which blocks click-jacking tricks.
Worked example 2 – Nashik Valley Grapes (fictional exporter): the forgotten .git and .env
The developer of a fictional Nashik grape exporter's website ran git clone straight into /var/www/mysite, and a .env file with a test password is also sitting there. Test from your laptop:
curl -I http://<PUBLIC_IP>/.git/config # before: 200 OK (anyone can download it)
curl -I http://<PUBLIC_IP>/.env # before: 200 OK
# ... add the dot-file location block, nginx -t, reload ...
curl -I http://<PUBLIC_IP>/.git/config # after: 403 Forbidden
curl -I http://<PUBLIC_IP>/.env # after: 403 Forbidden
curl -I http://<PUBLIC_IP>/.well-known/acme-challenge/test.txt # still allowed (Certbot needs it, Chapter 13)
How the rule works: location ~ /\.(?!well-known) is a regular-expression location. It matches any path that contains /. followed by anything except well-known, and deny all; answers 403. The real fix is still to keep only public files in the web root – but the rule is a safety net for the day someone forgets.
Common mistake – an add_header inside a location block silently drops the others
Nginx passes add_header lines from the server block down to a location block only if that location has no add_header of its own. Suppose a student adds a cache header for images:
location ~* \.(jpg|png|css)$ {
add_header Cache-Control "public, max-age=86400"; # now ONLY this header is sent for images and CSS
}
curl -I http://<PUBLIC_IP>/style.css now shows Cache-Control but no X-Content-Type-Options or X-Frame-Options. Fix: repeat the security headers inside that location (or keep them in a small file and include it in every location that has its own add_header), then sudo nginx -t && sudo service nginx reload and check with curl -I again.
Ravindra Bagale's Tip
Many students git clone the whole project, including the .git folder, into the web root – and anyone can download /.git/config. Keep only public files in the web root, or block dot-files. I tell every batch this – remember it.
Ravindra Bagale's Tip – मराठी
बरेच students .git folder सकट पूर्ण project web root मध्ये git clone करतात – आणि कोणीही /.git/config download करू शकतो. Web root मध्ये फक्त public files ठेवा, किंवा dot-files block करा. हे मी प्रत्येक batch ला सांगतो, लक्षात ठेवा.
Ravindra Bagale's Tip – हिंदी
बहुत से students .git folder समेत पूरा project web root में git clone कर देते हैं – और कोई भी /.git/config download कर सकता है. Web root में सिर्फ़ public files रखो, या dot-files block करो. यह मैं हर batch को बताता हूँ, याद रखो.
Practice task
Before and after hardening, run curl -I http://<PUBLIC_IP> and compare headers. Create /var/www/mysite/.env with dummy text and confirm it returns 403.
Thodkyaat sangaycha tar
- Upload to home,
sudo cpto/var/www/<site>, folders755, files644. - Amazon Linux: site files in
conf.d/; Ubuntu:sites-available+ enable (symlink ora2ensite), disable the default site. - Document root: Nginx
root; ApacheDocumentRoot+ matching<Directory>. SELinux label for paths outside/var/www. - New port:
listen/Listen, security group rule, SELinux port label on CentOS. - Index page:
index/DirectoryIndex; keep listing off. - Harden: hide versions, security headers, block dot-files. Test before reload, every time.
Got it? Your website is live – congratulations! Part 3 is complete. Now let's move on to dynamic websites – PHP, LAMP and LEMP.
समजलं का? तुमची वेबसाइट लाइव्ह आहे – अभिनंदन! Part 3 पूर्ण झाला. आता पुढे जाऊया डायनॅमिक वेबसाइट्सकडे – PHP, LAMP आणि LEMP.
समझ आया? आपकी वेबसाइट लाइव है – बधाई हो! Part 3 पूरा हुआ. अब आगे चलते हैं डायनेमिक वेबसाइट्स की ओर – PHP, LAMP और LEMP.