linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
  • Introduction
  • Lessons
  • How it works
  • Simulator
  • Knowledge base
  • Interview prep
Index
Categories
All entries
Footer
linuxlab-TutorialsPricingAboutPrivacy & cookies
Copyright © 2026 LinuxLab. All rights reserved.
home/linux/kb/Protocols/apache-httpd

kb/protocols ── Protocols ── intermediate

Apache httpd: the web server

Apache httpd is a web server. On RHEL the package is `httpd`, config `/etc/httpd/conf/httpd.conf`. On Debian/Ubuntu the package is `apache2`, config `/etc/apache2/apache2.conf` + `sites-enabled/`.

view as markdownaka: apache, httpd, apache2, apache-http-server

Apache vs nginx, when to use which

Modern Linux has two mainstream web servers:

  • Apache: process-per-request (worker/event MPM), modular, .htaccess, PHP through mod_php. The standard for shared hosting, the LAMP stack, legacy.
  • nginx: event-driven, lighter on resources, simpler config, better for static + reverse proxy.

Apache still makes sense when:

  • You need .htaccess edits made by users without a restart.
  • You want the mod_* ecosystem (mod_wsgi, mod_authnz_ldap, mod_security).
  • A ready-made product expects Apache (ZendServer, cPanel, mailman).

Otherwise reach for nginx or Caddy.

Installation, package differences

DistributionPackageDaemonConfigrun-as
RHEL/Fedorahttpdhttpd/etc/httpd/conf/httpd.conf + conf.d/apache
Debian/Ubuntuapache2apache2/etc/apache2/apache2.conf + sites-enabled/www-data
bash
# RHEL
sudo dnf install httpd
sudo systemctl enable --now httpd
# Debian
sudo apt install apache2
sudo systemctl enable --now apache2

Below httpd is used for brevity. On Debian replace it with apache2.

Directory layout on Debian (the "sites-available" model)

/etc/apache2/
├── apache2.conf                 ← main file, reads all the others
├── ports.conf                   ← Listen 80, 443
├── conf-available/              ← fragments
├── conf-enabled/                ← symlinks to active ones
├── mods-available/              ← all modules (.load + .conf)
├── mods-enabled/                ← symlinks to active modules
├── sites-available/             ← virtual host configs
│   ├── 000-default.conf
│   └── example.org.conf
└── sites-enabled/               ← symlinks to active ones
    └── example.org.conf -> ../sites-available/example.org.conf

Activation through helper utilities:

bash
sudo a2ensite example.org           # symlink into sites-enabled
sudo a2dissite example.org          # remove it
sudo a2enmod ssl rewrite            # enable modules
sudo a2dismod autoindex
sudo systemctl reload apache2

RHEL has no such split. All configs are dropped into /etc/httpd/conf.d/*.conf, modules load through LoadModule in /etc/httpd/conf.modules.d/*.conf.

Process model, why root and www-data at the same time

Apache starts as root so it can open [[capabilities|ports below 1024]] (80, 443 require CAP_NET_BIND_SERVICE), then forks workers as an unprivileged user (www-data or apache).

bash
ps -ef | grep httpd
# root      1234   1  0  /usr/sbin/httpd -DFOREGROUND     ← parent (master)
# apache    1235 1234  0 /usr/sbin/httpd -DFOREGROUND     ← worker
# apache    1236 1234  0 /usr/sbin/httpd -DFOREGROUND     ← worker

Workers handle the requests. If one of them holds a vulnerability, the exploit gets only apache permissions, not root. So /var/www/html/ must be readable by apache but not writable (otherwise RCE leads to a disk write).

VirtualHost, several sites on one IP

One host can serve several domains on one IP by the Host: header from HTTP/1.1.

apache
# /etc/apache2/sites-available/example.org.conf
<VirtualHost *:80>
    ServerName    example.org
    ServerAlias   www.example.org
    DocumentRoot  /var/www/example.org
    ErrorLog  ${APACHE_LOG_DIR}/example.org-error.log
    CustomLog ${APACHE_LOG_DIR}/example.org-access.log combined
    <Directory /var/www/example.org>
        Options -Indexes +FollowSymLinks
        AllowOverride All           # allow .htaccess
        Require all granted
    </Directory>
</VirtualHost>

Gotchas:

  • The first VirtualHost is the default: requests with an unknown Host: fall into it. Often you want a default config that returns 444 to defend against "spam requests to the IP".
  • *:80 vs IP:80: * catches every interface, an explicit IP only that one.
  • <Directory> vs <Location> vs <Files>: a directory on disk vs a URL path vs a file pattern. Mixing them up is dangerous: access control rules may silently fail to apply.

HTTPS, mod_ssl + Let's Encrypt

bash
# 1. Enable the module
sudo a2enmod ssl
sudo systemctl reload apache2
# 2. Get a certificate (certbot writes the config itself)
sudo apt install certbot python3-certbot-apache
sudo certbot --apache -d example.org -d www.example.org
# certbot creates:
# /etc/apache2/sites-available/example.org-le-ssl.conf

A minimal SSL VirtualHost by hand:

apache
<VirtualHost *:443>
    ServerName     example.org
    DocumentRoot   /var/www/example.org
    SSLEngine on
    SSLCertificateFile      /etc/letsencrypt/live/example.org/fullchain.pem
    SSLCertificateKeyFile   /etc/letsencrypt/live/example.org/privkey.pem
    # Modern ciphers
    SSLProtocol             all -SSLv3 -TLSv1 -TLSv1.1
    SSLCipherSuite          HIGH:!aNULL:!MD5
    SSLHonorCipherOrder     on
</VirtualHost>

The tls-handshake itself has its own article.

Reverse proxy for a backend application

apache
<VirtualHost *:80>
    ServerName api.example.org
    ProxyPreserveHost On
    ProxyPass        / http://127.0.0.1:8000/
    ProxyPassReverse / http://127.0.0.1:8000/
    RequestHeader set X-Forwarded-Proto "http"
</VirtualHost>

You need the proxy and proxy_http modules:

bash
sudo a2enmod proxy proxy_http

The backend (uvicorn/gunicorn/etc.) listens on loopback:8000, Apache on 80/443 serves TLS, headers, and rate limits.

.htaccess, per-directory rules without a reload

Apache's main feature is the .htaccess file: in any directory it can hold config directives and applies WITHOUT a server restart. Handy for shared hosting where the user gets only FTP access.

apache
# /var/www/example.org/.htaccess
Options -Indexes
ErrorDocument 404 /404.html
RewriteEngine On
RewriteRule ^old-page$ /new-page [R=301,L]

For this to work, the VirtualHost must set AllowOverride All for the directory. The default is None (.htaccess is ignored).

The cost: Apache reads .htaccess on every request in that hierarchy. Under high load, turn it off and move the rules into the main config.

Management

bash
sudo systemctl reload httpd                 # re-read config without dropping connections
sudo systemctl restart httpd                # full restart
sudo apachectl configtest                   # syntax check (== httpd -t)
sudo apachectl -S                           # show all VirtualHosts and their rules
sudo apachectl -M                           # list loaded modules

Always run configtest before reload. Otherwise a syntax error stops the daemon from restarting, and the site goes down.

Logs

RHEL: /var/log/httpd/{access,error}_log. Debian: /var/log/apache2/{access,error}.log.

bash
sudo tail -f /var/log/apache2/error.log     # errors in real time
sudo tail -f /var/log/apache2/access.log    # requests

The combined format (default):

192.168.0.5 - - [29/Apr/2026:14:23:11 +0300] "GET / HTTP/1.1" 200 1234 "-" "Mozilla/5.0..."
 ↑                                            ↑                  ↑   ↑
 client                                       request            code size

The logrotate config is usually /etc/logrotate.d/httpd, rotating daily/weekly.

Security, the required minimum

  1. Do not run as root post-fork: confirm User apache / User www-data is set in the config.
  2. Hide the version: ServerTokens Prod + ServerSignature Off in apache2.conf.
  3. Disable directory listing: Options -Indexes for every <Directory>.
  4. Block .git/.env: <DirectoryMatch "/\\.git"> + Require all denied.
  5. HSTS: Header always set Strict-Transport-Security "max-age=63072000".
  6. fail2ban on error.log to ban whoever keeps hammering 404 and 401.
  7. mod_security + the OWASP ruleset, a WAF for the common attacks.

Debugging, the typical problems

bash
# 1. Running?
systemctl status httpd
ss -tlnp 'sport = :80'      # is it listening on the port?
# 2. Where the logs go in real time
sudo tail -F /var/log/httpd/error_log
# 3. Which VirtualHost matches a name
sudo apachectl -S | grep example.org
# 4. A simple curl test
curl -I http://localhost
curl -kvI https://example.org

Frequent errors:

  • 403 Forbidden on a clean site points to SELinux on RHEL. Check ls -Z /var/www/html, you need the httpd_sys_content_t context. sudo restorecon -R /var/www/html.
  • Permission denied in the log means apache/www-data cannot read the DocumentRoot. chmod o+rX -R or the right ACL.
  • Address already in use at startup means someone else is already on 80 (ss -tlnp 'sport = :80' finds it).

§ команды

bash
sudo apachectl configtest

Syntax check of the config before reload, mandatory, or the site goes down

bash
sudo apachectl -S

Show all VirtualHosts and which one is the default, diagnoses matching

bash
sudo a2ensite example.org && sudo systemctl reload apache2

Activate a VirtualHost from sites-available on Debian, symlink + reload

bash
sudo certbot --apache -d example.org

Get a Let's Encrypt certificate and configure the SSL VirtualHost automatically

bash
sudo tail -F /var/log/apache2/error.log

Real-time error log, the main tool for debugging 500/403/permissions

§ см. также

  • tls-handshakeTLS HandshakeTLS is the encryption layer above TCP. Before data flows, both sides run a handshake: they exchange keys, verify the certificate, and agree on a cipher.
  • http-protocolHTTP/1.1, HTTP/2, HTTP/3HTTP/1.1 is a text-based protocol with keep-alive. HTTP/2 is binary with multiplexing over a single TCP connection. HTTP/3 carries HTTP/2 semantics over QUIC/UDP without TCP head-of-line blocking.
  • cmd-curlcurl: HTTP client from the terminal`curl` is a CLI for HTTP, HTTPS, FTP, and more. Send requests, inspect headers, certificates, and timing. The primary tool for REST API debugging.
  • fail2banfail2ban: automatic bans from logsfail2ban reads logs (sshd, nginx, postfix), uses a regex to catch N failed attempts in a window, and adds the IP to firewall rules for bantime. It is the main tool against SSH brute-force.
  • systemdsystemd: the init system and service managersystemd is the Linux init system: PID 1 that starts everything else, tracks dependencies, restarts what crashes, and collects the logs.

§ упоминается в уроках

  • ›intermediate-13-apache-vhosts
Footer
linuxlab-
Copyright © 2026 LinuxLab. All rights reserved.
Tutorials
Pricing
About
Privacy & cookies