You install a web server and hit a wall
You run dnf install httpd, start the service, and point your browser to localhost. The connection times out. You open the firewall, reload the page, and get a 403 Forbidden error. You check the logs and see a stream of SELinux access denials. The daemon is running, but nothing gets through. This happens because Fedora ships with layered security that blocks unconfigured services by design. You must align the package, the service manager, the network filter, and the mandatory access control system before the first request reaches your document root.
What is actually happening
A web server on Fedora does not run in isolation. The package manager drops binaries into /usr/lib. Systemd manages the process lifecycle and socket activation. Firewalld controls inbound traffic at the kernel level using zone-based rules. SELinux enforces file and process boundaries through security contexts. When you install httpd or nginx, you only receive the binaries and default configuration files. The service remains inactive. The firewall drops inbound traffic on port 80. SELinux refuses to let the web server process read files outside /var/www/html. You have to connect these pieces manually. Think of it like setting up a storefront. You purchased the building, but you still need to unlock the doors, open the gate, and tell the security guard which customers can enter which rooms. Fedora expects you to configure each layer explicitly. Trust the package manager. Manual file edits drift, snapshots stay.
Install and start the service
Here is how to pull the package from the official repositories and register it with systemd.
sudo dnf install httpd -y # pulls the Apache binary, default configs, and dependency chain
sudo systemctl enable --now httpd # creates the boot symlink and starts the process immediately
For Nginx, swap httpd for nginx in both commands. The service manager handles the rest. Fedora keeps user modifications in /etc/httpd/ while the package owns /usr/lib/httpd/. Always edit the /etc path. Package updates will overwrite /usr/lib without warning. Run systemctl status httpd to verify the unit is active. The status output shows recent log lines and the current state in one view. Always check status before restart.
Open the firewall
Fedora enables firewalld on first boot. The default public zone drops everything except SSH and DHCP. You need to whitelist HTTP and HTTPS before external clients can reach the server.
sudo firewall-cmd --permanent --add-service=http # adds port 80 to the persistent configuration
sudo firewall-cmd --permanent --add-service=https # adds port 443 to the persistent configuration
sudo firewall-cmd --reload # applies the persistent rules to the running kernel firewall
Run firewall-cmd --reload after every rule change. Otherwise the runtime config and the persistent config diverge, and your changes vanish on reboot. The --permanent flag writes to the XML zone file on disk. The --reload flag tells the daemon to merge that file into the active netfilter rules. You can verify the change with firewall-cmd --list-services. The output should include http and https.
Configure a virtual host
The default config serves files from /var/www/html. Real projects need their own directory and domain mapping. Create a new configuration file in the drop-in directory.
sudo mkdir -p /etc/httpd/conf.d # ensures the drop-in directory exists
sudo nano /etc/httpd/conf.d/mysite.conf # opens a new config file for your domain
Paste the following block into the editor.
<VirtualHost *:80>
ServerName mysite.example.com # tells Apache which domain this block handles
DocumentRoot /var/www/mysite # points to the directory containing your site files
ErrorLog /var/log/httpd/mysite-error.log # isolates error output from the main log
CustomLog /var/log/httpd/mysite-access.log combined # tracks incoming requests separately
</VirtualHost>
Create the document root and drop a test file inside it.
sudo mkdir -p /var/www/mysite # creates the directory structure for the site
echo '<h1>Hello from Fedora</h1>' | sudo tee /var/www/mysite/index.html # writes a test page
sudo systemctl reload httpd # tells the running daemon to re-read its configuration files
Check the service status before you restart it. systemctl status httpd shows recent log lines and the current state in one view. If the reload fails, the status output will point to the exact syntax error. Apache validates configuration files on reload. A single missing slash or unclosed tag will abort the transaction. Fix the syntax before proceeding.
Fix SELinux contexts
SELinux will block Apache from reading /var/www/mysite until you assign the correct security label. The default context only applies to /var/www/html.
sudo semanage fcontext -a -t httpd_sys_content_t "/var/www/mysite(/.*)?" # defines the rule for the path
sudo restorecon -Rv /var/www/mysite # applies the rule to existing files and prints each change
If your application needs to write uploads or cache files, you must grant write access separately.
sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/mysite/uploads(/.*)?" # allows read and write
sudo restorecon -Rv /var/www/mysite/uploads # applies the writable label to the upload directory
SELinux denials show up in journalctl -t setroubleshoot with a one-line summary. Read those before disabling SELinux. Disabling it removes the guardrails and exposes the system to privilege escalation. The semanage command modifies the local policy store. The restorecon command applies those rules to the filesystem. You must run both. Skipping restorecon leaves the files with their old labels. Skipping semanage means the labels will revert after a package update or filesystem relabel.
Enable TLS with Certbot
Modern browsers refuse to load sites without HTTPS. Certbot automates the certificate request and renewal process.
sudo dnf install certbot python3-certbot-apache -y # installs the client and the Apache plugin
sudo certbot --apache -d mysite.example.com # runs the interactive setup and modifies your vhost
Certbot automatically edits your virtual host configuration to enable HTTPS and sets up a systemd timer to renew the certificate before it expires. The timer runs twice a day and only renews when the certificate is within thirty days of expiration. You do not need to add a cron job. The --apache plugin rewrites your virtual host file to redirect HTTP to HTTPS and attach the certificate paths. If you prefer manual control, use certbot certonly and edit the configuration yourself. The automated plugin saves time but modifies files you own. Review the changes after the first run.
Verify it worked
Here is how to confirm the stack is functioning end to end.
curl -I http://localhost # checks for a 200 OK response and correct headers
sudo journalctl -xeu httpd # shows recent logs with explanatory text for the service
sudo ausearch -m avc -ts recent # lists any recent SELinux access vector cache denials
The curl command should return HTTP/1.1 200 OK. The journal output should show Started The Apache HTTP Server. with no failed dependencies. The ausearch command should return nothing. If it returns lines, your SELinux contexts are still misaligned. Run journalctl -xe first. Read the actual error before guessing.
Common pitfalls and what the error looks like
The curl command will return curl: (7) Failed to connect to localhost port 80: Connection refused when the service is not running or the firewall is blocking it. Check systemctl status httpd first. The service might have failed to start because of a config syntax error.
You will see 403 Forbidden in the browser when SELinux blocks the document root. The Apache error log will contain [client] AH01630: client denied by server configuration. The journalctl -t setroubleshoot output will show SELinux is preventing httpd from read access on the directory. Run the semanage and restorecon commands above to fix it.
The certbot command will fail with Authentication failed: The client lacks sufficient authorization when the firewall blocks port 80 or another service is already listening on it. Stop the conflicting service and ensure firewall-cmd --list-services includes http. Certbot needs to place a temporary file in /.well-known/acme-challenge/ to prove domain ownership. If Apache cannot write to that path, the challenge fails.
You might encounter AH00526: Syntax error on line 14 of /etc/httpd/conf.d/mysite.conf during reload. Apache stops parsing at the first error. Check for missing closing tags, duplicate ServerName directives, or typos in the DocumentRoot path. Run sudo httpd -t to validate the configuration without restarting the daemon. Fix the syntax before proceeding.
When to use this vs alternatives
Use Apache when you need .htaccess overrides, complex URL rewriting, or a large ecosystem of third-party modules. Use Nginx when you are serving static assets, handling high concurrent connections, or acting as a reverse proxy. Use a container runtime like Podman when you want isolated environments with built-in networking and volume mounts. Use a managed hosting provider when you do not want to maintain the operating system or patch security updates. Stay on the upstream Workstation or Server edition if you only deviate from the defaults occasionally.