Running an HTTP backend on the same machine as an Apache-hosted site usually hits one obstacle: Apache already owns ports 80 and 443. The standard workaround is to give Apache the job of reverse-proxying a sub-domain to your backend, which keeps a single public entry point and lets Apache handle TLS.
This assumes a Linux VM with a domain such as domain.com mapped to it, Apache installed and running, and a backend service you want to expose via a sub-domain like sub.domain.com.
Enabling mod_proxy
First, enable Apache's proxy module and restart the service:
$ sudo a2enmod proxy proxy_http $ sudo systemctl restart apache2
Creating the virtual host
Sub-domains usually get their own configuration file under /etc/apache2/sites-available. Create a file there, say sub.domain.com.conf, containing:
<VirtualHost *:80>
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:5000/
ProxyPassReverse / http://127.0.0.1:5000/
ServerName sub.domain.com
ServerAdmin [email protected]
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
This tells Apache that requests to sub.domain.com should be forwarded to a service running locally on port 5000. The target can just as easily be a different port or a remote host.
Register the configuration and restart Apache:
$ sudo a2ensite sub.domain.com.conf $ sudo systemctl restart apache2
Testing the backend
With Apache configured, run the backend service on port 5000. A simple header-debugging server works for verification:
$ go run http-server-debug-request-headers.go -addr 127.0.0.1:5000 2023/01/17 01:01:20 Starting server on 127.0.0.1:5000
From a separate terminal on the same machine, test it with curl:
$ curl 127.0.0.1:5000/headers hello /headers
The server's terminal should show the incoming request logging:
2023/01/17 01:02:50 127.0.0.1:42406 GET /headers Host: 127.0.0.1:5000 User-Agent: curl/7.81.0 Accept: */*
From any machine, the sub-domain should now reach the backend:
$ curl http://sub.domain.com/headers hello /headers
Apache listens on port 80 for domain.com; when it sees a request for sub.domain.com, it proxies that request to the service listening on port 5000. If something fails, check both Apache's error log and access log for clues.
TLS for the sub-domain
If the main domain already uses Let's Encrypt via certbot, adding the sub-domain is nearly automatic. Since a new Apache configuration now exists, run certbot again:
$ sudo certbot --apache
Follow the prompts. certbot will detect the new virtual host, obtain a certificate for sub.domain.com, and HTTPS access follows:
$ curl https://sub.domain.com/headers hello /headers
The backend itself serves plain HTTP, but it's not reachable from outside the machine—port 5000 typically isn't exposed publicly. Apache terminates TLS and forwards HTTP to the backend, which is a common pattern.
One subtlety: certbot's HTTP challenge needs to serve a special file under a path like .well-known/acme-challenge to prove domain ownership. At first glance, that seems impossible when all requests to the sub-domain are proxied to the backend. However, certbot handles it transparently: during the handshake it temporarily adds a RewriteRule to sub.domain.com.conf, routing requests starting with .well-known/acme-challenge to a local directory it controls. Once verification succeeds, it removes those rules quietly.



