Short answer: run sudo apt install apache2 on Ubuntu or Debian. Apache starts right away and serves its default page on port 80. Open HTTP and HTTPS in ufw, put each site in /etc/apache2/sites-available, enable it with a2ensite, check with apache2ctl configtest and reload. For HTTPS, run certbot --apache. On Rocky Linux and AlmaLinux the package is httpd.
sudo apt update
sudo apt install -y apache2
systemctl status apache2 --no-pager
apache2ctl -vThe paths, helper scripts and messages below follow Debian’s apache2 packaging (version 2.4.68-1~deb12u1 in the Debian 12 package index on our server), Ubuntu’s packaging and the documentation on httpd.apache.org, read on 21 October 2026. The Debian ufw profile names (WWW Full, OpenSSH) were checked in /etc/ufw/applications.d on Debian 12 with ufw 0.36.2; the Ubuntu ones (Apache, Apache Secure, Apache Full) come from Ubuntu’s apache2 packaging. Apache itself was not run for this guide, so outputs are marked as examples. The newest upstream release is 2.4.69 (1 October 2026); distributions keep their own 2.4.x and backport security fixes, so apache2ctl -v shows a lower number on a fully patched server.
Open ports 80 and 443 in ufw
Open ports 80 and 443: 443 for HTTPS, 80 for redirects and for Let’s Encrypt’s HTTP check. Ubuntu’s apache2 package adds three ufw profiles; Debian’s does not, but ufw itself ships a profile with the same ports:
sudo ufw allow OpenSSH # first, so you keep your SSH session
sudo ufw allow "Apache Full" # Ubuntu only: 80 and 443
sudo ufw allow "WWW Full" # Debian only: the same ports, from ufw's own profile
sudo ufw enable
sudo ufw app list| Profile | Ports | Comes with |
|---|---|---|
| Apache | 80/tcp | Ubuntu's apache2 |
| Apache Secure | 443/tcp | Ubuntu's apache2 |
| Apache Full | 80,443/tcp | Ubuntu's apache2 |
| WWW Full | 80,443/tcp | Debian's ufw package (Ubuntu's ufw does not install it) |
Then open http://YOUR-SERVER-IP/. The “Apache2 Debian Default Page” (or the Ubuntu one) is /var/www/html/index.html. More rules, such as limiting SSH to your own address, are in the ufw guide.
The Debian and Ubuntu layout
Debian splits Apache’s configuration into small files and gives you scripts that switch them on and off with symlinks, so you rarely edit the main file:
| Path | What it holds |
|---|---|
/etc/apache2/apache2.conf | Main configuration: global settings, directory permissions, includes |
/etc/apache2/ports.conf | The Listen lines (80, and 443 when mod_ssl is on) |
sites-available/ · sites-enabled/ | One file per site; enabled ones are symlinks. 000-default.conf is the default site |
mods-available/ · mods-enabled/ | Module load lines and their settings |
conf-available/ · conf-enabled/ | Extra snippets such as security.conf or the PHP-FPM handler |
/etc/apache2/envvars | Run user (www-data) and APACHE_LOG_DIR, read by apache2ctl, which the systemd unit calls |
/var/log/apache2/ | access.log, error.log, other_vhosts_access.log |
| Task | Command |
|---|---|
| Enable or disable a site | a2ensite example.com · a2dissite 000-default |
| Enable or disable a module | a2enmod rewrite · a2dismod autoindex |
| Enable or disable a config snippet | a2enconf servername · a2disconf serve-cgi-bin |
| Check the syntax | sudo apache2ctl configtest |
| List virtual hosts and which file defines them | sudo apache2ctl -S |
| List loaded modules | sudo apache2ctl -M |
| Apply site or config changes | sudo systemctl reload apache2 |
| Apply module changes | sudo systemctl restart apache2 |
$ sudo a2enmod rewrite
Enabling module rewrite.
To activate the new configuration, you need to run:
systemctl restart apache2The scripts suggest a restart for modules and a reload for sites and config snippets. Run apache2ctl configtest before either: a reload with a broken file keeps the old configuration running, but a restart with one leaves Apache stopped. The full list of service commands is in systemctl commands.
Set up a virtual host for your domain
- 1Point the domain at the server
Create A (and AAAA) records for
example.comandwww.example.comwith your server’s address, as described in pointing a domain to a VPS. You can build the site while DNS propagates. - 2Create the document root
shell sudo mkdir -p /var/www/example.com/public echo '<h1>example.com works</h1>' | sudo tee /var/www/example.com/public/index.html - 3Write the site file
/etc/apache2/sites-available/example.com.conf <VirtualHost *:80> ServerName example.com ServerAlias www.example.com DocumentRoot /var/www/example.com/public <Directory /var/www/example.com/public> Options -Indexes +FollowSymLinks AllowOverride All Require all granted </Directory> ErrorLog ${APACHE_LOG_DIR}/example.com-error.log CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined </VirtualHost> - 4Enable it, test, reload
shell sudo a2ensite example.com sudo a2dissite 000-default sudo apache2ctl configtest sudo systemctl reload apache2 curl -H "Host: example.com" http://127.0.0.1/configtestshould end withSyntax OK. Thecurlline asks for the site by name before DNS has caught up.
AllowOverride All is there for .htaccess files. Debian’s apache2.conf sets AllowOverride None for /var/www/, so without this line every .htaccess rewrite is silently ignored; with it, also run sudo a2enmod rewrite. The Apache documentation recommends putting rules into the site file instead where you can, because with overrides allowed Apache looks for .htaccess in every directory on every request. Debian also only grants access to /var/www and /usr/share; a document root under /srv needs its own <Directory> block with Require all granted, or visitors get a 403 Forbidden.
The AH00558 ServerName warning
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1. Set the 'ServerName' directive globally to suppress this messageHarmless, but easy to silence with a global ServerName, ideally the server’s fully qualified domain name:
echo "ServerName server1.example.com" | sudo tee /etc/apache2/conf-available/servername.conf
sudo a2enconf servername
sudo systemctl reload apache2PHP: PHP-FPM with proxy_fcgi, or mod_php
Debian and Ubuntu run Apache with the event MPM, which handles many idle keep-alive connections with few threads. PHP-FPM keeps it that way:
sudo apt install -y php-fpm
sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.2-fpm
sudo systemctl restart apache2NOTICE: Not enabling PHP 8.2 FPM by default.
NOTICE: To enable PHP 8.2 FPM in Apache2 do:
NOTICE: a2enmod proxy_fcgi setenvif
NOTICE: a2enconf php8.2-fpm
NOTICE: You are seeing this message because you have apache2 package installed.Use the version your release ships: php8.3-fpm on Ubuntu 24.04, php8.4-fpm on Debian 13; ls /etc/apache2/conf-available/ | grep fpm shows the name. The snippet hands every .php file to the FPM socket in /run/php/. To test, put <?php phpinfo(); into a file in the document root, look for “Server API: FPM/FastCGI”, then delete the file. PHP settings now live in /etc/php/8.2/fpm/php.ini, not the Apache one, which is where most memory_limit changes go wrong.
| PHP-FPM + proxy_fcgi | mod_php (libapache2-mod-php) | |
|---|---|---|
| MPM | Event, the default | Switches Apache to prefork, because mod_php is not thread-safe |
| Memory | PHP only in its own pool of workers | A PHP interpreter in every Apache process, also for static files |
| Run each site as its own user | Yes, one pool per site | No, everything runs as www-data |
| Setup | Three commands | One package |
| Use it for | New servers | Old applications that rely on Apache-only PHP features |
With PHP in place, the database half of a LAMP stack is covered in installing MariaDB or MySQL.
HTTPS with Certbot
sudo apt install -y certbot python3-certbot-apache
sudo certbot --apache -d example.com -d www.example.com
sudo certbot renew --dry-runCertbot proves control of the domain over port 80, so DNS must already point at the server and the firewall must let port 80 in. It then writes a second site file, example.com-le-ssl.conf, enables mod_ssl and that site, and by default adds a redirect from HTTP to HTTPS. The Debian and Ubuntu package renews through a systemd timer (systemctl list-timers | grep certbot), and the dry run proves that renewal will work.
Logs and troubleshooting
sudo tail -f /var/log/apache2/error.log
sudo tail -f /var/log/apache2/example.com-access.log
sudo journalctl -u apache2 -n 50 --no-pagerThe error log names the file and the cause for most problems; when Apache does not start at all, journalctl has the reason. tail -f keeps the log open while you reload the page. Sites without their own CustomLog write to other_vhosts_access.log.
| Message or symptom | Cause | Fix |
|---|---|---|
(98)Address already in use: AH00072: make_sock: could not bind to address [::]:80 | nginx or another program holds port 80 | Find it with sudo ss -tlnp | grep :80; see address already in use |
AH00526: Syntax error on line 9 of …: Invalid command 'RewriteEngine' | The module behind the directive is off | sudo a2enmod rewrite |
AH01630: client denied by server configuration | No Require all granted for that directory | Add a Directory block, see above |
| .htaccess rules have no effect | AllowOverride None, or mod_rewrite off | AllowOverride All in the site, a2enmod rewrite |
| The browser downloads .php files or shows PHP code | No PHP handler enabled | a2enconf php8.2-fpm |
AH02454: FCGI: attempt to connect to Unix domain socket … failed | PHP-FPM is not running or the socket name differs | Start PHP-FPM; a 503 follows until then |
Rocky Linux and AlmaLinux: httpd
sudo dnf install -y httpd
sudo systemctl enable --now httpd
sudo firewall-cmd --permanent --add-service=http --add-service=https
sudo firewall-cmd --reload
sudo apachectl configtest| Debian · Ubuntu | Rocky · AlmaLinux | |
|---|---|---|
| Package and service | apache2 | httpd |
| Started after install | Yes | No: systemctl enable --now httpd |
| Main file | /etc/apache2/apache2.conf | /etc/httpd/conf/httpd.conf |
| Sites | sites-available + a2ensite | Any .conf file in /etc/httpd/conf.d/ |
| Modules | a2enmod / a2dismod | LoadModule lines in /etc/httpd/conf.modules.d/ |
| TLS module | Included: a2enmod ssl | Separate package: dnf install mod_ssl |
| Logs | /var/log/apache2/access.log | /var/log/httpd/access_log |
| Run user | www-data | apache |
| Firewall | ufw | firewalld |
| Extra layer | None | SELinux |
SELinux is the usual surprise. A document root outside /var/www needs the right label, and Apache may only open outgoing connections, for example to a backend it proxies to, once a boolean allows it:
sudo semanage fcontext -a -t httpd_sys_content_t "/srv/www(/.*)?"
sudo restorecon -Rv /srv/www
sudo setsebool -P httpd_can_network_connect 1Apache or nginx?
| Apache httpd | nginx | |
|---|---|---|
| .htaccess files | Supported; many PHP apps ship them | Not supported; rules go into the server config |
| PHP | PHP-FPM or mod_php | PHP-FPM only |
| Configuration | Directives per directory, many modules | One server block per site, compact |
| Reverse proxy and TLS front end | Capable (mod_proxy) | The common choice |
| Certbot plugin | --apache | --nginx |
| Pick it when | An app expects .htaccess, or you know Apache | You serve static files, proxy to apps, or start fresh |
Both are fast enough for nearly any site on one server. How the two differ under the hood is in what nginx is and how it works.
Running both: Apache behind nginx
Only one program can own port 80. A common split is nginx in front for TLS and static files and Apache behind it for the PHP sites whose .htaccess rules nobody wants to translate:
- 1Move Apache to localhost:8080
/etc/apache2/ports.conf and the site file # ports.conf: replace "Listen 80" Listen 127.0.0.1:8080 # sites-available/example.com.conf <VirtualHost 127.0.0.1:8080> ... SetEnvIf X-Forwarded-Proto https HTTPS=on </VirtualHost>The
SetEnvIfline tells PHP applications that the visitor used HTTPS, which prevents redirect loops in WordPress and similar apps. Disablemod_ssland the-le-sslsite if Certbot had set them up, since nginx now handles TLS. - 2Log the real visitor address
/etc/apache2/conf-available/remoteip.conf RemoteIPHeader X-Forwarded-For RemoteIPInternalProxy 127.0.0.1Run
sudo a2enmod remoteipandsudo a2enconf remoteip, and change%hto%ain theLogFormatlines ofapache2.conf, so the log shows the visitor instead of 127.0.0.1. - 3Proxy from nginx
/etc/nginx/sites-available/example.com server { listen 80; server_name example.com www.example.com; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }Restart Apache first, then reload nginx. TLS on the nginx side works as in nginx as a reverse proxy with SSL.
Apache on a VPS
- Memory: Apache with PHP-FPM, MariaDB and a small WordPress site fit on the smallest Standard KVM plan (2 cores, 4 GB RAM). With mod_php and prefork, every Apache process carries PHP, so the same traffic needs more RAM.
- Images: our Ubuntu and Debian VPS images take the apt commands on this page as written, with full root access and always-on DDoS protection in front.
- No server to run: if you only need websites, our Plesk hosting includes free auto-renewing SSL and the WordPress Toolkit, and the web server is managed for you.
Frequently asked
How do I restart Apache on Ubuntu or Debian?
Run 'sudo systemctl restart apache2'. For changes to sites or config snippets 'sudo systemctl reload apache2' is enough and keeps open connections. Run 'sudo apache2ctl configtest' first: it should print 'Syntax OK', because a restart with a broken file leaves Apache stopped. On Rocky Linux and AlmaLinux the service is httpd.
Where is the Apache configuration file on Ubuntu?
The main file is /etc/apache2/apache2.conf, the Listen ports are in /etc/apache2/ports.conf, and each site has its own file in /etc/apache2/sites-available that a2ensite enables. Logs are in /var/log/apache2. On Rocky Linux and AlmaLinux the main file is /etc/httpd/conf/httpd.conf and sites go into /etc/httpd/conf.d.
Can Apache and nginx run on the same server?
Yes, but not on the same port. The usual setup moves Apache to 127.0.0.1:8080 in ports.conf and the site file, and lets nginx answer on ports 80 and 443 and pass requests to Apache with proxy_pass. Enable mod_remoteip so Apache logs the visitor's address instead of 127.0.0.1.
How do I check which Apache version is installed?
Run 'apache2ctl -v' on Ubuntu and Debian, or 'httpd -v' on Rocky Linux and AlmaLinux. Distributions keep their own 2.4.x release and backport security fixes, so the number is often lower than the newest upstream release (2.4.69 as of October 2026) even on a fully updated server.