Skip to main content

Apache Cheatsheet

By Dejan Panovski •Updated on • Download PDF

Apache HTTP Server at a glance: service commands, file layout on Ubuntu and RHEL, virtual hosts, modules, .htaccess, redirects, TLS, reverse proxy, and log formats.

Apache is packaged as apache2 on Ubuntu and Debian and as httpd on Fedora and RHEL, with different file layouts and helper tools. This cheatsheet covers service and apachectl commands, configuration paths on both families, virtual hosts, modules, directory access and .htaccess, redirects, TLS, reverse proxy settings, access control, caching, logging, and common startup errors.

Service and CLI Commands

Use apache2 on Ubuntu and Debian, httpd on Fedora and RHEL.

CommandDescription
sudo systemctl reload apache2Apply a new configuration with a graceful restart
sudo systemctl restart apache2Stop and start the service
sudo systemctl start apache2Start the service
sudo systemctl stop apache2Stop the service
sudo systemctl status apache2Show the service state and recent log lines
sudo systemctl enable --now httpdStart now and at every boot on Fedora and RHEL
sudo apachectl configtestTest the configuration for syntax errors
sudo apachectl -t -D DUMP_INCLUDESList every configuration file that is loaded
sudo apachectl gracefulRestart workers after current requests finish
sudo apachectl graceful-stopStop after current requests finish
apachectl -vPrint the version
apachectl -VPrint the version, MPM, and build settings

Run configtest before every reload. On Ubuntu and Debian, call apache2ctl or apachectl rather than the apache2 binary, which needs the variables from /etc/apache2/envvars. On Fedora and RHEL, use sudo httpd -t -D DUMP_INCLUDES, httpd -v, and httpd -V for these diagnostic options; the packaged apachectl does not forward arbitrary arguments to httpd.

Configuration Layout

The two package families lay out the configuration differently.

PathDescription
/etc/apache2/apache2.confMain file on Ubuntu and Debian
/etc/apache2/ports.confListen directives on Ubuntu and Debian
/etc/apache2/sites-available/Virtual host files on Ubuntu and Debian
/etc/apache2/mods-available/Module load and config snippets
/etc/apache2/conf-available/Global config snippets
/etc/httpd/conf/httpd.confMain file on Fedora and RHEL
/etc/httpd/conf.d/*.confVirtual hosts and snippets on Fedora and RHEL
/etc/httpd/conf.modules.d/Module loading on Fedora and RHEL
/var/www/htmlDefault document root on both
/var/log/apache2/Logs on Ubuntu and Debian
/var/log/httpd/Logs on Fedora and RHEL

Apache runs as www-data on Ubuntu and Debian and as apache on Fedora and RHEL. Give that user read access to the document root.

Sites, Modules, and Config Snippets

Ubuntu and Debian helpers that manage the *-enabled symlinks. Before switching MPMs, migrate to PHP-FPM and disable mod_php if it is enabled.

CommandDescription
sudo a2ensite example.comEnable example.com.conf
sudo a2dissite 000-defaultDisable the default site
sudo a2enmod rewriteEnable a module
sudo a2dismod autoindexDisable a module
sudo a2enconf servernameEnable servername.conf
sudo a2disconf serve-cgi-binDisable a config snippet
sudo apachectl -MList loaded modules
sudo apachectl -SList virtual hosts and which one is the default
sudo a2dismod mpm_prefork && sudo a2enmod mpm_eventSwitch to event; test and restart afterward

Site files must end in .conf. After an MPM change, run configtest and restart the service. Fedora and RHEL use conf.d/ for snippets and LoadModule lines in conf.modules.d/. Inspect modules with sudo httpd -M and virtual hosts with sudo httpd -S.

Virtual Hosts

Directives that decide which site answers a request.

DirectiveDescription
<VirtualHost *:80> ... </VirtualHost>Define a site on port 80
ServerName example.comPrimary host name for the site
ServerAlias www.example.com *.example.comAdditional host names
DocumentRoot /var/www/example.com/public_htmlDirectory served for the site
DirectoryIndex index.html index.phpFile served when a directory is requested
ServerAdmin webmaster@example.comAddress shown on some error pages
ErrorLog ${APACHE_LOG_DIR}/example.com-error.logPer-site error log on Ubuntu and Debian
CustomLog ${APACHE_LOG_DIR}/example.com-access.log combinedPer-site access log
Listen 8080Accept connections on another port

When no ServerName or ServerAlias matches, Apache serves the first virtual host defined for that address and port. On Ubuntu and Debian that is usually 000-default.conf, because files load in alphabetical order.

Directory Access and .htaccess

Control what Apache may serve from a directory and what .htaccess may change.

DirectiveDescription
<Directory /var/www/example.com> ... </Directory>Apply settings to a directory tree
Require all grantedAllow access
Require all deniedDeny access
Options -IndexesTurn off directory listings
Options +FollowSymLinksFollow symbolic links
AllowOverride NoneIgnore .htaccess files
AllowOverride AllLet .htaccess override any directive
AllowOverride FileInfo AuthConfigAllow rewrites and auth only
<FilesMatch "\.(env|ini|log)$">
Require all denied
</FilesMatch>
Block sensitive file types

.htaccess is read on every request in every parent directory, which costs performance. Put rules in the virtual host when you control the server, and keep AllowOverride None where .htaccess is not needed.

Redirects and Rewrites

Redirect comes from mod_alias. The Rewrite* directives need a2enmod rewrite.

DirectiveDescription
Redirect permanent / https://example.com/301 redirect that keeps the path
Redirect 302 /old /newTemporary redirect for one path
RedirectMatch 301 ^/blog/(.*)$ /news/$1Regex redirect that keeps the tail
RewriteEngine OnEnable the rewrite engine for this context
RewriteCond %{HTTPS} offMatch only plain HTTP requests
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]Redirect to HTTPS
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]Match the www host, case-insensitive
RewriteRule ^ https://%1%{REQUEST_URI} [L,R=301]Redirect to the bare domain
RewriteCond %{REQUEST_FILENAME} !-fContinue only when no file exists
RewriteRule ^ index.php [L]Front controller in .htaccess or <Directory>

Prefer Redirect for plain redirects. In .htaccess, patterns omit the leading slash: ^old/ instead of ^/old/. Use the final two rows together only in .htaccess or <Directory> context, where REQUEST_FILENAME is a filesystem path.

HTTPS and TLS

Terminate TLS on port 443. Enable mod_ssl with sudo a2enmod ssl on Ubuntu and Debian, or install the mod_ssl package on Fedora and RHEL.

DirectiveDescription
<VirtualHost *:443> ... </VirtualHost>Define the TLS site
SSLEngine onTurn on TLS for the virtual host
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pemCertificate and intermediate chain
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pemPrivate key
SSLProtocol -all +TLSv1.2 +TLSv1.3Allow only modern protocol versions
Protocols h2 http/1.1Enable HTTP/2, needs mod_http2
Header always set Strict-Transport-Security "max-age=63072000"Send HSTS, needs mod_headers
sudo certbot --apache -d example.com -d www.example.comIssue and install a certificate
sudo a2ensite default-sslEnable the bundled self-signed TLS site

The prefork MPM severely restricts HTTP/2 concurrency. Prefer mpm_event; if mod_php is enabled, migrate PHP to PHP-FPM and disable mod_php before switching MPMs.

Reverse Proxy

Forward requests to an application. Enable the modules with sudo a2enmod proxy proxy_http headers.

DirectiveDescription
ProxyPass / http://127.0.0.1:3000/Forward requests to a local application
ProxyPassReverse / http://127.0.0.1:3000/Rewrite Location headers in redirects
ProxyPreserveHost OnPass the original Host header
RequestHeader set X-Forwarded-Proto "https"Tell the application TLS was used
ProxyPass /ws ws://127.0.0.1:3000/wsProxy WebSockets, needs mod_proxy_wstunnel
ProxyPass / http://127.0.0.1:3000/ upgrade=websocketWebSocket upgrade in mod_proxy_http on 2.4.47+
ProxyPass /static !Exclude a path from proxying, list it first
ProxyTimeout 300Wait longer for a slow backend
ProxyRequests OffKeep forward proxying disabled

mod_proxy adds X-Forwarded-For, X-Forwarded-Host, and X-Forwarded-Server automatically. Order matters: Apache uses the first ProxyPass that matches, so put specific paths before /.

Access Control and Hardening

Limit who reaches a path and what the server reveals. Use htpasswd -c only for a new file, because it overwrites an existing one.

DirectiveDescription
Require ip 10.0.0.0/8Allow a network
Require not ip 203.0.113.5Block an address inside <RequireAll>
AuthType BasicTurn on HTTP basic authentication
AuthName "Restricted"Realm shown in the login prompt
AuthUserFile /etc/apache2/.htpasswdPoint to the password file
Require valid-userAccept any user in the file
sudo htpasswd -c /etc/apache2/.htpasswd adminCreate a new password file
sudo htpasswd /etc/apache2/.htpasswd editorAdd or update a user
LimitRequestBody 67108864Cap request bodies at 64 MB
ServerTokens ProdSend only Apache in the Server header
ServerSignature OffRemove the version footer from error pages
TraceEnable OffDisable the TRACE method

On Ubuntu and Debian, ServerTokens, ServerSignature, and TraceEnable live in /etc/apache2/conf-available/security.conf.

Compression and Caching

Cut response size with mod_deflate and set browser caching with mod_expires and mod_headers.

DirectiveDescription
sudo a2enmod deflate expires headersEnable the modules on Ubuntu and Debian
AddOutputFilterByType DEFLATE text/html text/css application/javascriptCompress these types
AddOutputFilterByType DEFLATE application/json image/svg+xmlCompress JSON and SVG
ExpiresActive OnTurn on expiry headers
ExpiresByType image/webp "access plus 1 month"Cache images for a month
ExpiresByType text/css "access plus 1 year"Cache CSS for a year inside the asset block below
<Directory /var/www/assets>
Header set Cache-Control "public, max-age=31536000, immutable"
</Directory>
Cache fingerprinted public assets in this directory
Header unset ETagDrop ETags when a CDN or long expiry handles caching
FileETag NoneStop generating ETags

The directory must contain only fingerprinted public assets. Keep the one-year CSS expiry and ETag settings in this block, away from HTML and application responses. Do not compress images, archives, or video that are already compressed.

Logging

Choose what gets recorded and where to watch it.

DirectiveDescription
ErrorLog ${APACHE_LOG_DIR}/error.logSet the error log file
LogLevel warnSet the error log level
LogLevel info ssl:warn rewrite:trace3Set levels per module
CustomLog ${APACHE_LOG_DIR}/access.log combinedWrite access logs in the combined format
LogFormat "%h %t \"%r\" %>s %b %D" timedDefine a custom format named timed
SetEnvIf Request_URI "^/health$" nologMark health checks
CustomLog ${APACHE_LOG_DIR}/access.log combined env=!nologSkip marked requests
sudo tail -f /var/log/apache2/error.logFollow errors live
sudo journalctl -u apache2 -fFollow service-level messages

Error log levels run emerg, alert, crit, error, warn, notice, info, debug, and trace1 to trace8. Use rewrite:trace3 to debug rewrite rules, and turn it off afterward because it logs every request.

Log Format Strings

Placeholders for LogFormat, the Apache counterpart to nginx variables.

FormatDescription
%hClient address
%aClient IP, after mod_remoteip when it is enabled
%uAuthenticated user
%tTime the request was received
%rFirst line of the request
%>sFinal status code
%bResponse size in bytes, - for zero
%DTime to serve the request in microseconds
%TTime to serve the request in seconds
%vCanonical ServerName of the virtual host
%{Referer}iReferer request header
%{User-Agent}iUser-Agent request header
%{X-Forwarded-For}iForwarding chain from a proxy

The combined format is %h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i".

Common Errors

Startup and request errors you will see in the error log or systemctl status.

ErrorFix
AH00558: Could not reliably determine the server's fully qualified domain nameAdd ServerName localhost in a config snippet
AH00072: make_sock: could not bind to address [::]:80Another process holds the port; find it with sudo ss -tlnp 'sport = :80'
Invalid command 'RewriteEngine'Enable the module with sudo a2enmod rewrite
Invalid command 'ProxyPass'Enable proxy and proxy_http
AH01630: client denied by server configurationAdd Require all granted to the <Directory> block
403 Forbidden with correct RequireFix filesystem permissions; Apache needs execute on every parent directory
.htaccess rules ignoredSet AllowOverride All for that directory
500 after editing .htaccessCheck the error log for the bad directive

AH00558 is only a warning. Apache still starts, but the message appears on every reload until ServerName is set.

Firewall

Open HTTP and HTTPS after installing Apache.

CommandDescription
sudo ufw allow 'Apache Full'Open ports 80 and 443 with UFW
sudo ufw allow 'Apache'Open port 80 only
sudo ufw allow 'Apache Secure'Open port 443 only
sudo ufw app info 'Apache Full'Show the ports in a profile
sudo firewall-cmd --permanent --add-service={http,https}Open both ports with firewalld
sudo firewall-cmd --reloadApply the firewalld change

Use these guides for the longer explanations behind these commands.

GuideDescription
Apache Commands: Manage and Troubleshoot Your Web ServerService, module, and diagnostic commands in detail
How to Start, Stop, and Restart Apache on LinuxReload versus restart on each distribution
How to Install Apache on Ubuntu 26.04Installation, firewall rules, and first steps
How to Set Up Apache Virtual Hosts on UbuntuHosting several sites on one server
Redirect HTTP to HTTPS in ApacheRedirect and rewrite patterns
How to Force HTTPS using .htaccessHTTPS redirects when you cannot edit the virtual host
Secure Apache with Let's EncryptCertbot setup and renewal
Configuring the Apache Error and Access LogsLog formats, levels, and rotation
Nginx CheatsheetThe same reference for nginx