Use Apache as a reverse proxy to forward external URLs to internal Docker containers by enabling the mod_proxy modules, defining VirtualHost directives with ProxyPass and ProxyPassReverse, and ensuring DNS resolves correctly to your server. This setup allows seamless access to services like Nextcloud or Portainer without exposing ports directly, providing a secure and centralized entry point for your local network applications while keeping container IPs hidden from external clients.
Prerequisites and Network Configuration
Before configuring Apache, you must ensure that your Docker containers are accessible within the same network as the Apache server. The most robust method involves creating a custom Docker network so that containers can communicate using their service names rather than dynamic IP addresses. This eliminates issues where container IPs change upon restart, which would otherwise break the proxy configuration.
Create a dedicated bridge network for your proxy-related containers:
docker network create proxy-net
When running your application containers, attach them to this network. For example, if you are hosting a web application named
myapp, start it with:
docker run -d --name myapp --network proxy-net myapp-image
Ensure that the Apache server itself is also connected to this network if it runs in a container, or configure its firewall to allow traffic on port 80 and 443. If Apache is installed on the host machine, it can reach the Docker network via the default bridge interface, but connecting Apache to the same custom network provides cleaner name resolution. You will need to know the hostname of the container as defined in the Docker Compose file or the
--name flag, as Apache will use this string to route requests.
Enabling Required Apache Modules
Apache requires specific modules to handle reverse proxying functionality. The core modules needed are
mod_proxy,
mod_proxy_http, and optionally
mod_ssl if you plan to terminate HTTPS connections locally before forwarding them over HTTP to your containers. On Debian-based systems, these can be enabled using the
a2enmod utility.
Run the following commands to enable the necessary modules:
sudo a2enmod proxy proxy_http proxy_balancer lbmethod_byrequests
sudo systemctl restart apache2
Verify that the modules are loaded by checking the Apache status or listing the available modules. If any module fails to load, check the error logs located at
/var/log/apache2/error.log. Missing modules often result in 503 Service Unavailable errors when attempting to access proxied sites. Additionally, ensure that the
AllowEncodedSlashes directive is configured if your application uses encoded slashes in URLs, though this is less common for standard web apps.
Configuring the Virtual Host
The core of the configuration lies in the Virtual Host definition. Create a new configuration file in the
/etc/apache2/sites-available/ directory, for example,
myapp.conf. Inside this file, define the server name, which should match the domain or subdomain you intend to use for accessing the container.
<VirtualHost *:80>
ServerName myapp.local
ServerAdmin [email protected]
# Enable reverse proxy
ProxyPreserveHost On
ProxyPass / http://myapp:8080/
ProxyPassReverse / http://myapp:8080/
# Logging
ErrorLog ${APACHE_LOG_DIR}/myapp-error.log
CustomLog ${APACHE_LOG_DIR}/myapp-access.log combined
</VirtualHost>
The
ProxyPass directive maps the root path
/ to the internal address of the Docker container. Here,
myapp refers to the container name or service name on the Docker network, and
8080 is the port exposed internally by the container. It is crucial to use the trailing slash in both the source and destination paths to ensure correct URL rewriting. The
ProxyPassReverse directive handles HTTP redirect responses from the backend, ensuring that browsers are redirected to the correct public URL rather than the internal container address.
Enable the site and disable the default site if necessary:
sudo a2ensite myapp.conf
sudo a2dissite 000-default.conf
sudo systemctl reload apache2
If you encounter issues with location headers not being forwarded correctly, refer to our guide on [Apache ProxyPassReverse not forwarding location response](/news/1065934/apache-proxypassreverse-not-forwarding-location-response) for troubleshooting specific header manipulation techniques.
DNS and Local Resolution
For local development or internal network access, you may not have a real DNS server. In such cases, modify the
/etc/hosts file on the client machine to resolve
myapp.local to the IP address of the Apache server. This ensures that when you type the URL into your browser, the request reaches Apache, which then forwards it to the Docker container.
If you are setting up a more complex environment with multiple services, consider integrating a custom DNS solution within Docker. For advanced setups involving secure tunnels and custom DNS records, see [Setting up Dockerized WireGuard with Custom DNS in Multi-Lab Environment](/news/1242040/dockerized-wireguard-with-custom-dns). This approach provides a scalable way to manage hostname resolution across different networks without relying on static host files.
Security and Best Practices
Always restrict access to the proxy unless explicitly intended for public use. Use Apache's
Require directives to limit access to specific IP ranges or authenticated users. For production environments, terminate SSL/TLS at the Apache level using
mod_ssl to encrypt traffic between the client and the proxy. Forward the encrypted traffic to the container over HTTP only if the container is trusted and isolated within the internal network.
Avoid exposing Docker daemon ports directly to the internet. By routing all traffic through Apache, you gain logging capabilities, access control, and the ability to serve static assets alongside proxied content. Regularly update Apache and Docker to patch security vulnerabilities. Monitor logs for unusual activity and implement fail2ban rules to protect against brute-force attacks on the proxy endpoints.
Frequently Asked Questions
Why am I getting a 502 Bad Gateway error?
This error typically indicates that Apache cannot connect to the upstream Docker container. Verify that the container is running and that the port specified in
ProxyPass matches the internal port of the service. Check Docker logs for startup failures and ensure the container is attached to the correct network.
Can I proxy multiple containers under different subdomains?
Yes, you can define separate VirtualHost blocks for each subdomain, such as
app1.local and
app2.local. Each block should point to a different container name and port combination. Ensure that your DNS or hosts file resolves all subdomains to the Apache server's IP address.
How do I handle WebSocket connections?
Standard HTTP proxying does not support WebSockets out of the box. You must enable
mod_proxy_wstunnel and add specific
ProxyPass rules for the WebSocket endpoints. Use the
ws:// and
wss:// schemes in your proxy directives to facilitate full-duplex communication channels.
What if my container uses a non-standard port?
You must explicitly specify the port in the
ProxyPass directive. For example, if the container listens on port 3000, configure
ProxyPass / http://container-name:3000/. Failure to include the port will cause Apache to attempt connection on the default HTTP port 80, resulting in connection refused errors.
Is it safe to expose Docker containers directly via Apache?
It is safer than exposing ports directly, but you must still secure the application inside the container. Apache acts as a gateway, allowing you to enforce authentication and rate limiting before requests reach the backend. Always keep Apache updated and restrict the VirtualHost permissions to minimize the attack surface.