Hardening Apache reduces the chance that routine internet noise—scanners, bots, and opportunistic exploits—turns a minor configuration mistake into data exposure or an avoidable outage.

Most Apache security work is about shrinking what the server advertises and accepts: loaded modules, reachable URLs, directory indexing, allowed HTTP methods, and the TLS/HTTP headers negotiated at the edge. Those controls live in the global config, per-virtual-host blocks, and sometimes per-directory rules, so the effective posture is the sum of many small decisions.

Configuration differs across platforms (for example, /etc/apache2/ on Debian-style systems versus /etc/httpd/ on RHEL-style systems), and even “safe” changes can break real applications. Apply one change at a time, validate syntax before reloads, and treat irreversible protections such as HSTS as a final step after HTTPS behavior is confirmed stable.

Steps to secure Apache web server:

  1. Inventory loaded Apache modules.
    $ apachectl -M
    Loaded Modules:
     core_module (static)
     so_module (static)
     watchdog_module (static)
     http_module (static)
     log_config_module (static)
     logio_module (static)
     version_module (static)
     unixd_module (static)
     access_compat_module (shared)
     alias_module (shared)
     auth_basic_module (shared)
     authn_core_module (shared)
     authn_file_module (shared)
     authz_core_module (shared)
     authz_host_module (shared)
     authz_user_module (shared)
     autoindex_module (shared)
     brotli_module (shared)
    ##### snipped #####

    Remove or disable modules not required by the site to reduce exposed features and directive surface area.

    On some systems the command is apache2ctl instead of apachectl.

  2. Check exposed server identity headers from the client side.
    $ curl -skI https://host.example.net/ | grep -iE '^(server|x-powered-by):'
    server: Apache

    Some headers come from the application stack (for example, PHP), not Apache itself.

  3. Confirm clear HTTP requests redirect to HTTPS.
    $ curl -I http://host.example.net/
    HTTP/1.1 301 Moved Permanently
    Date: Mon, 29 Dec 2025 06:54:29 GMT
    Server: Apache/2.4.58 (Ubuntu)
    Location: https://host.example.net/
    Content-Type: text/html; charset=iso-8859-1

    HSTS should be enabled only after HTTPS and redirects are confirmed stable, because browsers cache the policy.

  4. Probe legacy TLS protocol negotiation to ensure old versions are disabled.
    $ openssl s_client -connect host.example.net:443 -servername host.example.net -tls1
    2070A397FFFF0000:error:0A0000BF:SSL routines:tls_setup_handshake:no protocols available:../ssl/statem/statem_lib.c:104:
    CONNECTED(00000003)
    ---
    no peer certificate available
    ##### snipped #####

    A failure such as no protocols available indicates legacy TLS versions are disabled.

  5. Verify OCSP stapling status when using public certificates.
    $ openssl s_client -connect host.example.net:443 -servername host.example.net -status
    CONNECTED(00000003)
    OCSP response: no response sent
    ##### snipped #####

    Look for an OCSP response line to confirm whether stapling is active.

  6. Verify administrative endpoints are blocked or allowlisted.
    $ curl -I http://192.0.2.40/server-status
    HTTP/1.1 403 Forbidden
    Date: Mon, 29 Dec 2025 08:27:17 GMT
    Server: Apache
    Content-Type: text/html; charset=iso-8859-1

    Administrative endpoints should be blocked by default, or restricted to an allowlist.

  7. Confirm directory listings are disabled for served directories.
    $ curl -i http://host.example.net/mysite/
    HTTP/1.1 403 Forbidden
    Date: Mon, 29 Dec 2025 09:26:51 GMT
    Server: Apache
    Content-Length: 199
    Content-Type: text/html; charset=iso-8859-1
    
    <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
    <html><head>
    <title>403 Forbidden</title>
    ##### snipped #####

    Directory URLs should return application content, a controlled index page, or an error.

  8. Deny access to common sensitive files and hidden paths.
    $ curl -I -H "Host: host.example.net" http://127.0.0.1/.env
    HTTP/1.1 403 Forbidden
    Date: Mon, 29 Dec 2025 09:16:31 GMT
    Server: Apache
    Content-Type: text/html; charset=iso-8859-1
    
    $ curl -I -H "Host: host.example.net" http://127.0.0.1/.git/config
    HTTP/1.1 403 Forbidden
    Date: Mon, 29 Dec 2025 09:16:31 GMT
    Server: Apache
    Content-Type: text/html; charset=iso-8859-1

    Start with common leak targets such as dotfiles, VCS directories, and config backups.

  9. Limit request bodies to the smallest practical size.

    Oversized requests should fail quickly with 413 instead of consuming CPU, memory, or disk.

  10. Confirm unsafe HTTP methods are rejected when the application does not require them.
    $ curl -i -X TRACE -H 'Host: host.example.net' http://127.0.0.1/
    HTTP/1.1 405 Method Not Allowed
    Date: Mon, 29 Dec 2025 09:14:47 GMT
    Server: Apache
    Allow: 
    Content-Length: 222
    Content-Type: text/html; charset=iso-8859-1
    ##### snipped #####

    Unexpected 2xx responses on unsafe methods indicate the server accepts them.

  11. Verify browser-facing security headers are present on real responses.
    $ curl -I -H 'Host: host.example.net' http://127.0.0.1/ | grep -iE '^(content-security-policy|x-frame-options|x-content-type-options|referrer-policy):'
    X-Frame-Options: SAMEORIGIN
    Content-Security-Policy: frame-ancestors 'self'

    Header policies should match the application’s actual behavior to avoid breaking legitimate content.

  12. Configure basic abuse controls for hostile clients.

    Rate limits and connection limits should be validated under load to avoid blocking legitimate traffic.

  13. Deploy a web application firewall when exposed to untrusted traffic.

    Deploy in detection mode first when possible to reduce the risk of accidental outages.

  14. Review logs and rotation policies for security visibility.

    Ensure rotation and retention are configured before enabling verbose logging.

  15. Validate configuration syntax after each change.
    $ sudo apachectl configtest
    Syntax OK

    Prefer reload or graceful restarts when supported by the change.