Apache listens on port 80 and 443 by default, but I’ve had plenty of good reasons to run it somewhere else: multiple sites on distinct ports on the same box, avoiding a conflict with another service, exposing a staging environment on a non-standard port, or sitting Apache behind a reverse proxy that owns the standard ports itself.
Here’s how I change Apache’s listening port, plus the firewall and SELinux steps that are easy to forget.
Why I Change Apache’s Port
- Running multiple independent Apache instances or virtual hosts distinguished by port rather than domain.
- Avoiding conflicts when another service already occupies 80/443.
- Restricting public exposure — Apache on a non-standard, internal-only port, with a separate reverse proxy handling the public-facing standard ports.
- Development and staging environments where I’ve got multiple projects running at once on one machine.
Prerequisites
- Apache installed and running
- Root or sudo access
- Awareness of firewall rules and SELinux status (RHEL-based systems), both of which affect this change
Step 1: Update the Listen Directive
The Listen directive tells Apache which port(s) to bind to, independent of any specific virtual host.
Debian/Ubuntu: I edit /etc/apache2/ports.conf
RHEL/CentOS: I edit /etc/httpd/conf/httpd.conf
Listen 8080
To keep 80 and add 8080 simultaneously:
Listen 80
Listen 8080
To bind a port to a specific IP only:
Listen 192.168.1.10:8080
Step 2: Update the Virtual Host to Match
Every <VirtualHost> block has to specify the exact IP:port combination it should respond to, matching a Listen directive:
<VirtualHost *:8080>
ServerName example.com
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
If there’s a Listen directive but no matching <VirtualHost *:port> block, Apache accepts connections on that port but serves the wrong site — or the default one.
Step 3: Test and Restart
sudo apachectl configtest
sudo systemctl restart apache2
A restart, not just a reload, is required for Listen changes to actually take effect, since it involves binding to a new socket.
Step 4: Open the New Port in the Firewall
I’ve got a full post on this; the short version:
# firewalld
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload
# ufw
sudo ufw allow 8080/tcp
Step 5: SELinux Considerations (RHEL/CentOS/Fedora)
With SELinux enforcing, Apache can only bind to a predefined set of ports by default (80, 443, 8080, 8443, a few others). Binding to something outside that list fails even with correct firewall rules — I’ve been caught by this more than once.
I check currently permitted ports:
sudo semanage port -l | grep http_port_t
If my chosen port isn’t listed, I add it:
sudo semanage port -a -t http_port_t -p tcp 9090
If the port was already assigned to a different SELinux type, I use -m (modify) instead of -a (add).
Step 6: Verify
sudo ss -tulnp | grep apache2
curl -I http://localhost:8080/
From another machine, once firewall rules are confirmed:
curl -I http://your-server-ip:8080/
Running Apache on Ports Below 1024 as a Non-Root User
Ports under 1024 are “privileged” on Linux and normally need root to bind. Apache’s master process typically starts as root specifically to bind 80/443, then drops privileges to a less-privileged worker user (www-data/apache) for handling requests — that’s already handled automatically by the standard startup scripts, so I don’t need to do anything special for standard ports.
If I’m deliberately running Apache entirely as a non-root user (like in a container) and need it to bind below 1024, I use setcap on the httpd binary, or more often, I just use a port above 1024 and put a reverse proxy in front that forwards from port 80.
Real-World Use Cases
- Multiple projects on one dev server:
Listen 8080for Project A,Listen 8081for Project B, each with its own<VirtualHost>block. - Apache behind Nginx or a load balancer: Apache listens on an internal-only port while Nginx handles 80/443 and proxies requests through.
- Staging environments: exposing a staging instance on a distinct port to keep it separate from production without needing a whole separate server.
- Container deployments: mapping a container’s internal Apache port to a different host port via Docker’s port mapping.
Mistakes I’ve Made
- Changing
Listenbut forgetting to update the matching<VirtualHost>block’s port, so Apache accepts connections but serves the wrong content. - Forgetting to open the new port in the firewall, hitting “connection refused” from external clients even though
curl localhost:PORTworked fine locally. - On RHEL-based systems, forgetting the SELinux port context, which stopped Apache from starting entirely with a permission-denied error buried in the journal.
- Assuming
systemctl reloadwas enough — a full restart is needed forListenchanges. - Port conflicts with another already-running service — I always check first:
sudo ss -tulnp | grep 8080
Security Best Practices
- I don’t rely on a non-standard port as a security measure — port scanners find open ports in seconds regardless of the number.
- If I’m exposing Apache on a non-standard port for internal-only access, I still restrict it via firewall rules to trusted IP ranges rather than leaving it open to the whole internet.
- When Apache sits behind a reverse proxy on an internal port, I double-check that internal port isn’t also accidentally exposed externally by a misconfigured firewall or cloud security group.
- I keep TLS configuration consistent regardless of port — if a service is sensitive enough to need HTTPS on 443, I don’t downgrade to plain HTTP just because it’s on an alternate port.
Performance Considerations
- Changing the port itself has no meaningful performance impact in my experience; performance comes down to MPM configuration and resource limits, not the port number.
- If Apache’s behind a reverse proxy on a non-standard port, I make sure
mod_remoteipis configured (see my log format post) so Apache logs and application logic see the real client IP rather than the proxy’s address. - For containerized deployments, I minimize the number of exposed/mapped ports to keep the attack surface and firewall management simpler.
Troubleshooting
Apache fails to start after changing Listen port I check the systemd journal for the specific error:
sudo journalctl -u apache2 -n 50
Common causes: the port’s already in use by another process, or (on RHEL-based systems) SELinux is blocking the bind.
curl works locally but not from another machine Almost always a firewall issue — I confirm the new port is explicitly allowed both on the host firewall and any cloud provider security group.
Wrong site/content served on the new port I confirm a <VirtualHost *:PORT> block exists matching the new port exactly; Apache falls back to the first matching virtual host if there’s no exact match.
FAQs
Can Apache listen on multiple ports at once? Yes — multiple Listen directives and a matching <VirtualHost> block for each port/IP combination I want to serve.
Does changing the port affect existing URLs or bookmarks? Yes — visitors need the new port explicit in the URL (like http://example.com:8080/) unless a reverse proxy or redirect maps the standard port to the new one transparently.
Is it safe to run a production site on a non-standard port permanently? Technically yes, but it adds friction for users who expect standard ports and complicates certificate/firewall management. I mostly use it internally, or behind a reverse proxy that presents the standard ports externally.
Summary and Key Takeaways
- Changing Apache’s port means updating both the
Listendirective and the matching<VirtualHost>block’s port. - A full restart, not a reload, is needed for
Listenchanges to take effect. - Firewall rules and, on RHEL-based systems, SELinux port contexts both need updating or clients won’t connect despite correct Apache config.
- Non-standard ports are a convenience and architectural tool for me, not a security measure on their own.
References
- Apache Listen Directive: https://httpd.apache.org/docs/current/mod/mpm_common.html#listen
- Apache Virtual Host Documentation: https://httpd.apache.org/docs/current/vhosts/
- Red Hat SELinux Ports Documentation: https://access.redhat.com/documentation/