Skip to content
Web server hardening

Security linter for Apache and Nginx

Paste a block of Apache.htaccess or Nginx server config and get a security audit: weak SSL/TLS (TLSv1, TLSv1.1, RC4, 3DES), server_tokens on, ServerTokens Full, autoindex, exposed status (/server-status, /nginx_status), missing security headers, no rate limiting, listen 80 without HTTPS redirect, unrestricted root location. Auto-detects dialect. Severity-coded findings with normative references.

How to use the linter

  1. 1

    Get the config block

    Apache: cat /etc/apache2/sites-enabled/*.conf or the app's .htaccess. Nginx: cat /etc/nginx/sites-enabled/* or the relevant server {... } block. If you have multiple files (modular with include), paste the main block - the linter doesn't recursively resolve includes.

  2. 2

    Paste in the tool

    Original syntax, comments included (will be ignored). The linter auto-detects the dialect: presence of RewriteRule, <Directory>, Options -> Apache; presence of server {, location {, add_header -> Nginx. Manual override not supported (scope is zero-config).

  3. 3

    Lint

    Output: severity-ordered findings (critical/high/medium/low/info), with directive code or problematic pattern, risk description, suggested remediation, normative reference (NIS2 art. 21, OWASP Web Server Misconfig, Mozilla SSL Configuration Generator).

  4. 4

    Export report

    'Copy report' produces a textual summary you can paste in internal mail, ops tickets, security tickets. For PDF export use the browser's 'Print' (print-optimized CSS). Bonus: findings are action-oriented, can be converted into atomic tickets for the hardening pipeline.

Why linting is the layer before audit

Automated triage vs manual review. A 200-line Apache config or a 400-line nginx.conf is a great candidate for automated linting: most hardening errors are pattern matching (a missing directive, a clearly wrong value like ssl_protocols TLSv1). The linter catches 70-80% of typical cyber audit findings in seconds, leaving complex cases to manual review (Apache <Location> redundancies, nginx add_header overrides, multi-VirtualHost interactions).

What this linter actually checks. 25 specific checks across 5 families: SSL/TLS (enabled protocols, ciphers, HSTS, OCSP stapling), information disclosure (server tokens, server signature, X-Powered-By, autoindex), access control (location root, exposed admin pages, exposed status), security headers (X-Frame-Options, X-Content-Type-Options, CSP, Permissions-Policy emitted via add_header/Header), availability (rate limiting with limit_req nginx, listen 80 without HTTPS redirect).

Limits of linting. The tool doesn't recursively resolve include /etc/nginx/snippets/*.conf directives, so the config it sees is the one you paste, not the effective runtime composed of the includes. It doesn't evaluate interactions between compiled modules like mod_security, nor filesystem posture (permissions and ownership of /etc/nginx/sites-enabled/). For complete audits a server-side agent that inspects the runtime system (lynis and similar) or a consultant analyzing the system as a whole is needed.

Operational privacy. The config you paste is processed directly in the browser: useful for auditing internal systems, staging environments not exposed externally, and avoiding sending potentially sensitive configs (internal paths, plain credentials in misconfigured directives) to third parties.

Check families applied

SSL / TLS

  • Weak protocols: TLSv1, TLSv1.1, SSLv2, SSLv3 -> CRITICAL
  • Weak ciphers: RC4, 3DES, EXPORT, NULL -> HIGH
  • SSL session caching configured (performance + security)
  • OCSP stapling enabled

Information disclosure

  • Apache: ServerTokens Full/OS/Major/Minor -> INFO
  • Apache: ServerSignature On -> INFO
  • Nginx: server_tokens on (default!) -> INFO
  • Apache: Options +Indexes -> MEDIUM
  • Nginx: autoindex on -> MEDIUM

Access control

  • Apache: <Location /server-status> without restricted Require -> HIGH
  • Nginx: location /nginx_status without allow/deny -> HIGH
  • Apache: missing RemoveHandler.phtml (PHP executable) -> MEDIUM if applicable
  • Apache: AllowOverride All in <Directory /> -> MEDIUM

Security headers

  • X-Frame-Options missing -> HIGH
  • X-Content-Type-Options nosniff missing -> MEDIUM
  • HSTS (Strict-Transport-Security) missing on SSL vhost -> HIGH
  • Content-Security-Policy missing -> HIGH
  • Permissions-Policy missing -> MEDIUM
  • Referrer-Policy missing -> MEDIUM

Availability

  • Nginx: limit_req_zone + limit_req missing on sensitive endpoints (auth, register, password-reset) -> MEDIUM
  • Apache/Nginx: listen 80 without redirect to HTTPS -> HIGH
  • Nginx: client_max_body_size not specified (default 1MB, attention upload-friendly) -> INFO

Glossary

Technical terms used on this page, briefly explained.

Apache.htaccess #
Per-directory config file, Apache syntax. Allows sysadmins without main config access (e.g. shared hosting) to configure rewriting, headers, authentication. Slow (parsed on every request), but flexible. Disabled in high-perf scenarios with AllowOverride None.
Nginx server block #
Config block defining a virtual host. Syntax: server { listen...; server_name...; location / {... } }. Resolved at nginx startup, no per-request overhead. De facto standard for nginx in production.
TLSv1.0 / TLSv1.1 #
Deprecated TLS versions (RFC 8996, 2021). NIST and PCI DSS require TLSv1.2 minimum since 2018. Keeping them enabled for legacy clients is a risk (POODLE, BEAST, LUCKY13).
OCSP stapling #
Online Certificate Status Protocol stapling. The server includes the CA's OCSP response in the TLS handshake, avoiding the client contacting the CA to verify cert status (revoked? expired?). Reduces latency and improves privacy.
ServerTokens #
Apache directive controlling the Server header. Values: Full (default, exposes OS+modules+versions), Prod (only "Apache"), Major ("Apache/2"), Minor, Min, OS. Best practice: ServerTokens Prod.
limit_req_zone #
Nginx rate limiting directive. Defines a shared memory zone (leaky bucket storage) and a rate (e.g. 10 req/s). Applied via limit_req zone=name burst=20 nodelay; inside location {}.
AllowOverride #
Apache directive controlling which directives .htaccess can override. All = everything allowed (historic default, convenient, slow and opaque security). None =.htaccess ignored (high-perf, explicit security). FileInfo, AuthConfig, Limit, Indexes, Options = granular controls.
OCSP Must-Staple #
X.509 extension requiring a client to reject the certificate if OCSP stapling is missing. Mitigates downgrade attacks against OCSP. Not yet widely adopted due to self-DOS risk.

Frequently asked questions about the linter

Auto-detection of dialect is wrong: how do I force?
There's no manual force (KISS scope). The heuristic checks unique patterns: if you paste a block with server { or add_header -> Nginx; with RewriteRule or <Directory> -> Apache. If your config is too minimal to classify, the tool defaults to Apache (for retrocompat with minimal.htaccess). For Nginx without distinctive patterns: add a fictitious server { listen 443; } at the top to force detection.
Does the linter find known vulnerabilities (CVEs)?
No, it's a hardening linter, not a CVE scanner. For nginx core or Apache module CVEs: dedicated tools like lynis, nuclei with hardening templates, or commercial SaaS. The linter here covers static config posture: discovered misconfigurations (server_tokens on), missing items (header missing), known-problematic patterns (ssl_protocols TLSv1).
How do I find the rate limiting patterns I need?
For nginx, two layers. (1) Global anti-DOS: limit_req_zone $binary_remote_addr zone=global:10m rate=30r/s; in http {}, then limit_req zone=global burst=60 nodelay; in server {}. (2) Auth-specific: separate zone rate=5r/s for endpoints /login, /password-reset, /register. For Apache: external mod_evasive or mod_qos.
Does the linter analyze include files? (nginx /etc/nginx/snippets/*.conf)
No, it analyzes only the pasted block. To analyze modular configs: concatenate with cat /etc/nginx/sites-enabled/* /etc/nginx/snippets/*.conf and paste the result. Most findings are order-independent, so concatenation produces correct results.
Apache 2.4: is <code>Order allow,deny / Allow from all</code> deprecated?
Yes. Apache 2.4 replaced the 2.2 syntax with Require. Best practice: Require all granted (allow all), Require ip 192.168.0.0/16 (allow only local IPs), Require local (only localhost). The old syntax is supported via mod_access_compat but is deprecated.
Can I lint rsyslog, postfix, ssh configs? (not just web)
No, scope is Apache + Nginx (web servers). For other daemons there are dedicated linters (sshd -t for ssh config, postfix check for postfix, rsyslogd -N1 for rsyslog). For distributed cyber audit: lynis is the reference tool, free (bash script, no agent). Runs 200+ checks across all system layers.
The linter says 'CSP missing' but I emit it via Cloudflare Workers. How do I ignore?
The linter doesn't know what Cloudflare does. To handle out-of-scope false positives: the report is indicative, evaluate case by case. If you have defenses at a higher layer (CDN, WAF, reverse proxy), the finding is overrated by the linter but real risk is lower. Document it in the internal audit report with 'Mitigated at Cloudflare layer'.
Best tool for nginx + caching audit?
Auditing nginx in production is best done by combining several tools: nginx -V to inspect compiled modules and build flags, curl -I to inspect cache headers (X-Cache, Age, Cache-Control), and generic vulnerability scanners like nuclei with templates from the http/misconfiguration category to catch the most common hardening misconfigurations.
Does the linter cover Caddy, Traefik, HAProxy?
No. Scope is Apache + Nginx, the most common in PMI Italian businesses. Caddy has excellent default hardening (TLS forced, HSTS auto), needs minimal audit. Traefik uses a different middleware approach: headers are in YAML middleware, not server block. HAProxy has distinct syntax. Possible roadmap extension, not immediate priority.
Can I integrate the linter in CI/CD? Is there an API?
There's no API endpoint. The tool is a SPA. For CI: real server-side hardening linting is done with nginx -t (syntax validation), apache2ctl configtest, and dedicated tools like cfg-check for Apache. Security rule pattern matching is done with grep -E on known patterns, or with custom nuclei rules. The logic of this linter is open source (vanilla JS), reusable as a baseline for a CI script.

Want a full audit of your web servers?

This linter is a good starting point for automated triage. A professional infrastructure audit covers TLS termination, fail2ban, rate limiting, per-vhost isolation, PHP-FPM hardening, log rotation, monitoring. I work on unmanaged Linux VPS (Debian, Ubuntu, AlmaLinux, Rocky).

Audit your web servers

Want a realistic estimate for your project?

7-question wizard, 2 minutes, free. Output: range of person-days, rough price range, engagement recommendation. Reference rate 300 EUR/day. Built for custom backend projects, integrations, security audits or AI automation.

Get a free quote