How to Install and Configure Apache (httpd) on Fedora

Install Apache on Fedora using dnf, enable the httpd service, and configure it via /etc/httpd/conf/httpd.conf.

You just finished building a web project on Fedora

You finished wiring up a static site or a PHP backend on your Fedora machine. You want to expose it to your local network or the wider internet. You run a quick search, find a tutorial that references apache2, and copy-paste commands that fail silently. The service refuses to start. The firewall drops incoming packets. SELinux blocks directory access. You are left staring at a browser timeout and a wall of systemd warnings.

What is actually happening under the hood

Fedora ships Apache under the package name httpd. The name follows the historical Unix convention for network daemons. When you install it, the package manager places the binary in /usr/sbin/, drops the main configuration into /etc/httpd/conf/, and sets the default document root at /var/www/html/. The service runs as a systemd unit. It listens on ports 80 and 443 by default.

Think of the web server as a reception desk. The firewall is the building lobby. SELinux is the security guard checking badges. If the lobby door is locked, visitors never reach the desk. If the guard denies access to the files, the desk clerk cannot hand anything to the visitor. Fedora enables both the lobby lock and the security guard by default. You must explicitly configure both before traffic flows.

Configuration files in /etc/ are yours to modify. Files in /usr/lib/ ship with the package and get overwritten on updates. Always work in /etc/. Manual edits to /usr/lib/ drift away the moment you run dnf upgrade --refresh.

Install and launch the service

Here's how to pull the package from the default repositories and register the service with systemd.

sudo dnf install httpd -y
# Pulls the httpd package and all required dependencies from the base repo.
sudo systemctl enable --now httpd
# Registers the unit to start on boot and launches it immediately.

The enable flag creates a symlink in /etc/systemd/system/multi-user.target.wants/. The --now flag tells systemd to start the process right away. You do not need to run systemctl start separately.

Check the unit state before moving forward.

systemctl status httpd
# Shows the active state, recent log lines, and main process ID.

If the output shows active (running), the daemon is listening. If it shows failed, skip ahead to the troubleshooting section. Do not guess at configuration syntax before reading the actual error.

Open the firewall and adjust SELinux

The default firewall policy drops incoming HTTP and HTTPS traffic. You need to open the standard web ports before any browser can reach the server.

sudo firewall-cmd --permanent --add-service=http
# Adds port 80 to the persistent firewall rules.
sudo firewall-cmd --permanent --add-service=https
# Adds port 443 to the persistent firewall rules.
sudo firewall-cmd --reload
# Applies the persistent rules to the running firewall without dropping active connections.

Always run firewall-cmd --reload after every rule change. The runtime configuration and the persistent configuration diverge if you skip this step.

Fedora enforces mandatory access control on web directories. If you serve files from a custom path, the security context must match what Apache expects. The default context for web content is httpd_sys_content_t.

sudo semanage fcontext -a -t httpd_sys_content_t "/var/www/myapp(/.*)?"
# Tells SELinux to label this custom directory and all subdirectories as web-readable content.
sudo restorecon -Rv /var/www/myapp
# Applies the new context to existing files and prints each change.

If your application needs to write to a directory, such as an upload folder or a cache directory, you must add the httpd_sys_rw_content_t context. Apache will refuse to write to files labeled as read-only content.

Configure a virtual host

Apache reads configuration files from /etc/httpd/conf.d/ in alphabetical order. You should never edit the main httpd.conf for site-specific settings. Create a dedicated .conf file for your project.

# /etc/httpd/conf.d/myapp.conf
<VirtualHost *:80>
    ServerName example.com
    # Tells Apache which hostname this block applies to.
    DocumentRoot /var/www/myapp
    # Points the server to your custom directory.
    <Directory /var/www/myapp>
        Require all granted
        # Allows public access to the directory.
        Options -Indexes
        # Disables directory listing when no index file exists.
    </Directory>
</VirtualHost>

The Require all granted directive replaces the legacy Order allow,deny syntax. Apache 2.4 enforces explicit access control. If you omit the Require line, the server returns a 403 Forbidden response.

After writing the configuration, validate the syntax before restarting.

sudo httpd -t
# Parses all configuration files and reports syntax errors without starting the server.
sudo systemctl reload httpd
# Tells the running daemon to re-read configuration without dropping active connections.

Use reload instead of restart when you only change configuration files. A restart kills every active worker process. A reload gracefully hands off existing connections to new workers.

Verify the stack is responding

Run a lightweight request to confirm the server answers correctly.

curl -I http://localhost
# Requests only the headers to verify the server responds with HTTP 200.
curl -I http://example.com
# Tests the virtual host routing against your configured ServerName.

If curl returns HTTP/1.1 200 OK, the stack is operational. If you see HTTP/1.1 403 Forbidden, check the SELinux context and the Require directive. If you see HTTP/1.1 404 Not Found, verify the DocumentRoot path exists and contains an index.html or index.php file.

Check the access and error logs when something behaves unexpectedly.

journalctl -xeu httpd
# Reads the systemd journal with explanatory text and jumps to the end.
sudo tail -n 20 /var/log/httpd/error_log
# Shows the last twenty lines of the Apache-specific error log.

Most sysadmins type journalctl -xeu <unit> muscle-memory style. The x flag adds high-level explanations to priority messages. The e flag jumps to the end of the journal. The u flag filters by unit name.

Common pitfalls and what the error looks like

Configuration syntax errors stop the service from starting. Apache prints the exact line number and file path.

AH00526: Syntax error on line 12 of /etc/httpd/conf.d/myapp.conf:
Invalid command 'Require', perhaps misspelled or defined by a module not included in the server configuration

This error usually means you are missing the mod_authz_core module or you placed the directive outside a <Directory> or <Location> block. The Require directive belongs inside a directory or location context.

SELinux denials appear in the journal with a one-line summary.

type=AVC msg=audit(1698765432.123:456): avc:  denied  { read } for  pid=1234 comm="httpd" name="index.html" dev="sda1" ino=789012 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0

Read the tcontext field. If it shows user_home_t or default_t, the file is in the wrong location. Move it to /var/www/ or apply the correct context with semanage. Never disable SELinux to fix a web server. The denial tells you exactly which label is missing.

Port conflicts happen when another service already occupies port 80 or 443.

(98)Address already in use: AH00072: make_sock: could not bind to address [::]:80
no listening sockets available, shutting down

Run sudo ss -tlnp | grep :80 to identify the conflicting process. Stop or reconfigure the other service before launching Apache.

Firewall blocks manifest as connection timeouts rather than HTTP errors. The browser hangs until it gives up. Verify the service is open with sudo firewall-cmd --list-all. If http or https is missing from the services list, the firewall is dropping the packets before they reach the socket.

Run journalctl -xe first. Read the actual error before guessing. Half the time the symptom is a missing module or a typo in a directive.

When to use Apache versus other web servers

Use Apache when you need .htaccess support, extensive module compatibility, or legacy application requirements. Use Nginx when you are serving high-concurrency static assets or acting as a reverse proxy for backend services. Use Caddy when you want automatic HTTPS certificates and minimal configuration. Use the built-in PHP development server when you are debugging locally and do not need production-grade request handling.

Apache's process-driven MPM architecture handles blocking workloads well. Nginx's event-driven architecture handles thousands of idle connections with less memory. Caddy handles TLS provisioning automatically. Pick the tool that matches your traffic pattern and maintenance tolerance.

Where to go next

Trust the package manager. Manual file edits drift, snapshots stay.