We are working on hardening Apache web server on all app servers. As a part of this process we want to add some of the Apache response headers for security purpose. We are testing the settings one by one on all app servers. As per details mentioned below enable these headers for Apache:
- Install
httpdpackage onApp Server 2using yum and configure it to run on3000port, make sure to start its service. - Create an
index.htmlfile under Apache's default document root i.e/var/www/htmland add below given content in it.Welcome to the xFusionCorp Industries! - Configure Apache to enable below mentioned headers:
X-XSS-Protectionheader with value1; mode=blockX-Frame-Optionsheader with valueSAMEORIGINX-Content-Type-Optionsheader with valuenosniff
Note: You can test using curl on the given app server as LBR URL will not work for this task.
Configuring Apache with Security Headers on a Custom Port
A Deep Dive for System Administrators
Introduction
Web server hardening is a critical part of securing modern infrastructure. One of the most effective ways to harden Apache is by adding HTTP response headers that instruct browsers to enforce security policies. These headers protect against common web vulnerabilities such as cross-site scripting (XSS), clickjacking, and MIME-type sniffing attacks.
This guide walks through a complete implementation on a CentOS Stream 9 server. The task involves installing Apache, running it on a custom port, creating a test page, and enabling three specific security headers. Each step is explained in depth to build a strong understanding of the underlying concepts.
Environment Details
| Component | Value |
|---|---|
| Server | stapp02 |
| User | steve |
| Password | Am3ric@ |
| OS | CentOS Stream 9 |
| Apache Version | 2.4.62 |
| Custom Port | 3000 |
| Document Root | /var/www/html |
Understanding the Security Headers
Before diving into the configuration, it is essential to understand what each header does and why it matters.
X-XSS-Protection
This header enables the browser's built-in Cross-Site Scripting (XSS) filter. When set to 1; mode=block, the browser detects reflected XSS attacks and blocks the page from rendering rather than attempting to sanitize it. While modern browsers rely more on Content Security Policy, this header still provides protection for older browsers.
X-Frame-Options
This header prevents clickjacking attacks by controlling whether a page can be embedded in a frame or iframe. Setting it to SAMEORIGIN allows only pages from the same origin to embed the content. Attackers cannot trick users into clicking hidden elements on a different site.
X-Content-Type-Options
This header set to nosniff prevents browsers from guessing the MIME type of a file. Without this header, a browser might interpret a text file as JavaScript or HTML, leading to security vulnerabilities. Setting it to nosniff forces the browser to trust the server's declared Content-Type.
Summary Table
| Header | Value | Protection |
|---|---|---|
| X-XSS-Protection | 1; mode=block | Cross-site scripting |
| X-Frame-Options | SAMEORIGIN | Clickjacking |
| X-Content-Type-Options | nosniff | MIME sniffing |
Step 1: Connect to the Server
Access the server using SSH.
ssh steve@stapp02
Password: Am3ric@
Switch to root to perform administrative tasks.
sudo su -
Password: Am3ric@
The sudo command provides elevated privileges required for installing packages and editing system configuration files.
Step 2: Install the httpd Package
Install Apache using the yum package manager.
yum install -y httpd
The installation pulls in several dependencies.
| Package | Purpose |
|---|---|
| httpd | Main Apache server |
| httpd-core | Core server binary |
| httpd-tools | Utilities like apachectl |
| apr | Apache Portable Runtime |
| apr-util | Utility library for APR |
| mod_http2 | HTTP/2 protocol support |
| mod_lua | Lua scripting support |
| mailcap | MIME type handling |
After installation, the httpd.service is registered with systemd.
Step 3: Configure Apache to Listen on Port 3000
By default, Apache listens on port 80. The task requires port 3000. The Listen directive in the main configuration file controls this.
Edit the file at /etc/httpd/conf/httpd.conf.
sed -i 's/^Listen 80/Listen 3000/' /etc/httpd/conf/httpd.conf
Verify the change.
grep "^Listen" /etc/httpd/conf/httpd.conf
Expected output.
Listen 3000
Why Use sed
The sed command performs a search and replace in place. The ^ anchors the match to the start of the line, ensuring only the Listen directive is changed and not other occurrences of 80.
Step 4: Create the Index Page
Create a simple index.html file in the document root.
echo "Welcome to the xFusionCorp Industries!" > /var/www/html/index.html
Verify the content.
cat /var/www/html/index.html
The document root /var/www/html is the default location from which Apache serves files. The DirectoryIndex directive in the configuration tells Apache to look for index.html when a directory is requested.
Step 5: Configure Security Headers
Apache uses the mod_headers module to manipulate HTTP headers. This module is enabled by default on CentOS Stream 9.
Create a dedicated configuration file in the conf.d directory.
cat > /etc/httpd/conf.d/security-headers.conf << 'EOF'
Header always set X-XSS-Protection "1; mode=block"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
EOF
Why Use a Separate Configuration File
Keeping custom configurations in /etc/httpd/conf.d/ is a best practice. Files in this directory are automatically included by the main configuration. This approach avoids modifying the main file and makes it easier to manage changes.
Understanding the Directives
| Directive | Meaning |
|---|---|
| Header always set | Adds the header to all responses |
| X-XSS-Protection | Header name |
| "1; mode=block" | Header value (quoted) |
The always keyword ensures the header is added even to error responses and redirects.
Step 6: Start and Enable the Apache Service
Start the service and configure it to start on boot.
systemctl start httpd
systemctl enable httpd
systemctl status httpd
The status output confirms the service is running and listening on port 3000.
● httpd.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; preset: disabled)
Active: active (running)
Main PID: 29475 (httpd)
Status: "Started, listening on: port 3000"
Understanding systemctl
| Command | Purpose |
|---|---|
| systemctl start | Starts the service immediately |
| systemctl enable | Configures the service to start on boot |
| systemctl status | Shows the current state |
Step 7: Verify Firewall Status
Check if firewalld is active.
systemctl status firewalld
In this environment, firewalld was not installed. If it were active, port 3000 would need to be allowed.
firewall-cmd --zone=public --add-port=3000/tcp --permanent
firewall-cmd --reload
Step 8: Verify the Headers
Test the configuration using curl.
curl -I http://localhost:3000/
The output shows the HTTP response headers.
HTTP/1.1 200 OK
Date: Thu, 24 Sep 2026 12:01:11 GMT
Server: Apache/2.4.62 (CentOS Stream)
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Last-Modified: Thu, 24 Sep 2026 11:59:33 GMT
ETag: "27-65c39553de0df"
Accept-Ranges: bytes
Content-Length: 39
Content-Type: text/html; charset=UTF-8
All three headers are present with the correct values.
Filter the output for specific headers.
curl -I http://localhost:3000/ | grep -E "X-XSS|X-Frame|X-Content"
Expected output.
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Complete Workflow Summary
# 1. Connect to the server
ssh steve@stapp02
sudo su -
# 2. Install httpd
yum install -y httpd
# 3. Configure port 3000
sed -i 's/^Listen 80/Listen 3000/' /etc/httpd/conf/httpd.conf
grep "^Listen" /etc/httpd/conf/httpd.conf
# 4. Create index.html
echo "Welcome to the xFusionCorp Industries!" > /var/www/html/index.html
# 5. Configure security headers
cat > /etc/httpd/conf.d/security-headers.conf << 'EOF'
Header always set X-XSS-Protection "1; mode=block"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
EOF
# 6. Start and enable Apache
systemctl start httpd
systemctl enable httpd
systemctl status httpd
# 7. Configure firewall (if applicable)
systemctl status firewalld
# If active:
# firewall-cmd --zone=public --add-port=3000/tcp --permanent
# firewall-cmd --reload
# 8. Verify
curl -I http://localhost:3000/
curl -I http://localhost:3000/ | grep -E "X-XSS|X-Frame|X-Content"
Verification Checklist
| Check | Command | Expected |
|---|---|---|
| Apache installed | `rpm -qa \ | grep httpd` |
| Listening on 3000 | `ss -tlnp \ | grep :3000` |
| Index page | cat /var/www/html/index.html |
Welcome message |
| Headers file | cat /etc/httpd/conf.d/security-headers.conf |
Three headers |
| Service running | systemctl is-active httpd |
active |
| Service enabled | systemctl is-enabled httpd |
enabled |
| Headers present | curl -I http://localhost:3000/ |
All three headers |
Troubleshooting
Apache Fails to Start
Check the configuration syntax.
httpd -t
Check the error log.
tail -20 /var/log/httpd/error_log
journalctl -u httpd -n 50
Headers Not Appearing
Verify that mod_headers is enabled.
httpd -M | grep headers
If not enabled, uncomment the LoadModule line in /etc/httpd/conf.modules.d/00-base.conf.
Port Already in Use
Check what is listening on port 3000.
ss -tlnp | grep :3000
Stop the conflicting service or choose a different port.
Firewall Blocking Access
Check firewalld status and allow the port if needed.
systemctl status firewalld
firewall-cmd --zone=public --add-port=3000/tcp --permanent
firewall-cmd --reload
SELinux Blocking Port
If SELinux is enforcing, Apache may not be able to bind to port 3000.
semanage port -a -t http_port_t -p tcp 3000
Best Practices
- Use a separate configuration file in
/etc/httpd/conf.d/for custom headers. - Always test configuration with
httpd -tbefore restarting. - Use
Header always setto ensure headers appear on all responses. - Document the purpose of each header for future reference.
- Check both the service status and the actual HTTP response after changes.
- Consider Content Security Policy (CSP) for modern XSS protection.
- Regularly review and update security headers as standards evolve.
Conclusion
This guide demonstrated the complete process of installing Apache, configuring it to run on a custom port, creating a test page, and enabling three important security headers. Each step was explained with the underlying concepts to build a deep understanding.
The key takeaways are that HTTP response headers provide browser-enforced security controls, Apache configuration is modular and can be extended via files in /etc/httpd/conf.d/, and verification with tools like curl confirms that the configuration is working as intended.
By applying these headers, the web server gains protection against cross-site scripting, clickjacking, and MIME-type sniffing attacks, contributing to a more secure application environment.
Quick Reference
| Task | Command |
|---|---|
| Install httpd | yum install -y httpd |
| Change port | sed -i 's/^Listen 80/Listen 3000/' /etc/httpd/conf/httpd.conf |
| Create index | echo "Welcome..." > /var/www/html/index.html |
| Add headers | Create /etc/httpd/conf.d/security-headers.conf
|
| Start service | systemctl start httpd |
| Enable service | systemctl enable httpd |
| Test headers | curl -I http://localhost:3000/ |
| Check syntax | httpd -t |
Top comments (0)