Apache Reverse Proxy and Certbot SSL: A Troubleshooting Experience

Recently, I set up my web applications behind an Apache reverse proxy with SSL provided by Certbot. I faced several challenges along the way—from conflicting default pages to browser caching issues—and I want to share my experience and final solution to help anyone facing similar problems.


Background

I had two internal applications that I needed to expose securely:

  • dify.ruianding.com: My application running on an internal port 27360 needed to be reverse proxied to this domain on port 443.
  • json.ruianding.com: Another application running on port 8888 should be accessible via this domain on port 443, with all requests automatically redirected from the root to /editor.

Note: My applications are running inside Docker containers on my local client machine. I use Apache as a reverse proxy to forward requests from these containers to the public domain, enhancing security by not exposing container ports directly.

Initially, I used FRP to map internal ports to public ports, but to enhance security, I decided to only expose the applications through Apache reverse proxy and handle SSL with Certbot.


The Issues I Encountered

1. Apache Default Page Conflict

When I first configured Apache for dify.ruianding.com, I encountered an unexpected behavior: despite having a custom reverse proxy configuration, accessing the domain still showed Apache’s default page.


Investigation:

  • I discovered that Certbot automatically generated a default virtual host configuration file (dify-le-ssl.conf) for SSL, which did not include the reverse proxy settings.
  • My custom configuration (dify.conf) also existed, but Apache was prioritizing the default certbot-generated config.

2. Browser Caching Problems

After making adjustments to disable the default site, I still couldn’t see the correct page on my desktop browser. Switching to my phone revealed the intended content.
Conclusion:

  • The issue was ultimately due to browser cache. Clearing the cache or using an incognito window resolved the problem.

3. Concerns about Certbot Renewal

I was also worried that by disabling the Certbot default virtual host, the certificate renewal process might fail.
Finding:

  • Certbot relies on the ACME challenge response. As long as a correctly configured virtual host for the domain exists (i.e., one that handles the /.well-known/acme-challenge/ requests), renewal proceeds without issue.

Final Solution and Configuration

A. Configuring dify.ruianding.com

I merged the necessary reverse proxy directives into the SSL virtual host configuration. For example, my final /etc/apache2/sites-enabled/dify-le-ssl.conf looked like this:

<IfModule mod_ssl.c>
<VirtualHost *:443>
    ServerName dify.ruianding.com

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/dify.ruianding.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/dify.ruianding.com/privkey.pem
    Include /etc/letsencrypt/options-ssl-apache.conf

    # Reverse proxy configuration: forward requests to internal port 27360
    ProxyPreserveHost On
    ProxyPass / http://127.0.0.1:27360/
    ProxyPassReverse / http://127.0.0.1:27360/
</VirtualHost>
</IfModule>

I ensured that the necessary Apache modules were enabled:

sudo a2enmod proxy proxy_http ssl rewrite
sudo systemctl reload apache2

B. Configuring json.ruianding.com with a Redirection to /editor

For json.ruianding.com, I created a separate virtual host file (e.g., /etc/apache2/sites-enabled/json.ruianding.com.conf) with both HTTP and HTTPS configurations:

<VirtualHost *:80>
    ServerName json.ruianding.com
    # Redirect HTTP to HTTPS
    RewriteEngine On
    RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</VirtualHost>

<VirtualHost *:443>
    ServerName json.ruianding.com

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/json.ruianding.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/json.ruianding.com/privkey.pem
    Include /etc/letsencrypt/options-ssl-apache.conf

    # Redirect root requests to /editor
    RewriteEngine On
    RewriteCond %{REQUEST_URI} ^/$
    RewriteRule ^/?$ https://%{HTTP_HOST}/editor [R=301,L]

    # Reverse proxy configuration: forward requests to internal port 8888
    ProxyPreserveHost On
    ProxyPass / http://127.0.0.1:8888/
    ProxyPassReverse / http://127.0.0.1:8888/

    ErrorLog ${APACHE_LOG_DIR}/json_error.log
    CustomLog ${APACHE_LOG_DIR}/json_access.log combined
</VirtualHost>

C. Using Certbot for SSL Certificates and Auto-Renewal

1. Install Certbot and Apache Plugin

sudo apt update
sudo apt install certbot python3-certbot-apache

2. Obtain SSL Certificates

Run the following command for each domain. For example, for json.ruianding.com:

sudo certbot --apache -d json.ruianding.com

During the process, you’ll be prompted to enter your email, agree to the terms, and choose whether to redirect HTTP to HTTPS (choose to redirect).

3. Test Auto-Renewal

After obtaining the certificates, test the auto-renewal process:

sudo certbot renew --dry-run

If the test runs successfully, Certbot will handle certificate renewals automatically.


Conclusion

In summary, I resolved the issues by:

  • Disabling the unwanted Certbot-generated default virtual host to ensure the custom reverse proxy configuration was used.
  • Clearing browser cache to see the updated content.
  • Verifying that disabling the default page did not affect the Certbot renewal process, as the active virtual host correctly handled the ACME challenges.

This experience taught me the importance of carefully managing virtual host configurations and remembering that sometimes the simplest issues—like browser caching—can cause a lot of headaches. I hope this blog post helps others in navigating similar challenges when setting up Apache reverse proxies with Certbot SSL.