Certbot Apache plugin overwrites existing SSL VirtualHosts when multiple vhosts are defined in one file

Hello Team,

I am experiencing an issue with the Certbot Apache plugin and would like to confirm whether this is a known limitation or an unintended bug.


:small_blue_diamond: My domain is:

domain1.example.com
domain2.example.com
domain3.example.com

:small_blue_diamond: I ran this command:

certbot --apache -d domain1.example.com
certbot --apache -d domain2.example.com

:small_blue_diamond: It produced this output:

Certificate successfully received.
Certificate is saved at:
/etc/letsencrypt/live/domain1.example.com/fullchain.pem

Deploying Certificate to VirtualHost /etc/apache2/sites-enabled/domain1-le-ssl.conf
Congratulations! You have successfully enabled HTTPS.

The command completes successfully with no errors.


:small_blue_diamond: My web server is (include version):

Apache/2.4.x

:small_blue_diamond: The operating system my web server runs on is (include version):

Ubuntu 22.04 LTS

:small_blue_diamond: My hosting provider, if applicable, is:

Self-managed VPS / Cloud VM

:small_blue_diamond: I can login to a root shell on my machine:

Yes

:small_blue_diamond: I'm using a control panel to manage my site:

No (manual Apache configuration)

:small_blue_diamond: The version of my client is:

certbot 5.x.x (installed via snap)

:gear: Apache Configuration (HTTP only)

I have multiple HTTP VirtualHosts defined in a single file:

File:
/etc/apache2/sites-available/multi-vhost.conf

<VirtualHost *:80>
    ServerName domain1.example.com
    DocumentRoot /var/www/domain1

    <Directory /var/www/domain1>
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/domain1-error.log
    CustomLog ${APACHE_LOG_DIR}/domain1-access.log combined
</VirtualHost>

<VirtualHost *:80>
    ServerName domain2.example.com
    DocumentRoot /var/www/domain2

    <Directory /var/www/domain2>
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/domain2-error.log
    CustomLog ${APACHE_LOG_DIR}/domain2-access.log combined
</VirtualHost>

<VirtualHost *:80>
    ServerName domain3.example.com
    DocumentRoot /var/www/domain3

    <Directory /var/www/domain3>
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/domain3-error.log
    CustomLog ${APACHE_LOG_DIR}/domain3-access.log combined
</VirtualHost>

All domains work correctly over HTTP (port 80).


:cross_mark: Unexpected Behavior with Certbot

Step 1 – First certificate

certbot --apache -d domain1.example.com

:check_mark: Certbot creates a new SSL VirtualHost correctly
:check_mark: SSL works for domain1.example.com


Step 2 – Second certificate

certbot --apache -d domain2.example.com

:cross_mark: Certbot does not create a new SSL VirtualHost
:cross_mark: It modifies or overwrites the existing SSL config for domain1
:cross_mark: Port 80 vhosts are merged or moved into the same SSL file
:cross_mark: Certificates are overwritten or shared unintentionally


:magnifying_glass_tilted_left: Result

  • SSL VirtualHosts are not isolated per domain
  • Re-running Certbot affects previously installed certificates
  • Apache configuration becomes inconsistent

:red_question_mark: Questions

  1. Is this a known limitation of the Certbot Apache plugin when multiple HTTP VirtualHosts are defined in one file?
  2. Is there any supported Certbot version or configuration that avoids this behavior?

This directive points to a file with certificate data in PEM format, or the certificate identifier through a configured cryptographic token. If using a PEM file, at minimum, the file must include an end-entity (leaf) certificate. The directive can be used multiple times (referencing different filenames) to support multiple algorithms for server authentication - typically RSA, DSA, and ECC. The number of supported algorithms depends on the OpenSSL version being used for mod_ssl: with version 1.0.0 or later, openssl list-public-key-algorithms will output a list of supported algorithms, see also the note below about limitations of OpenSSL versions prior to 1.0.2 and the ways to work around them.

Multiple directives for multiple names isn't foreseen by apache documentation (the source code I'm not sure)

Of course you can have multiple virtualhosts if you do it manually. Default certbot behavior should be to map one http vhost to one https vhost with one certificate. And it probably would've done just that had you not specified -d

1 Like

What you tried should have worked. I just setup a test with 2 VirtualHosts for port 80 in the same conf file. This conf was in sites-available and I ran a2ensite to setup the link to it in sites-enabled

The first certbot --apache -d test1.example.com created the port 443 VirtualHost in a new conf file simpletest-le-ssl.conf. In my test the Certbot message said it was created in sites-available. I checked with ls and a symlink was created in sites-enabled.

That is different than your message which said the le-ssl.conf got created in sites-enabled

The second certbot --apache -d test2.example.com added the port 443 VHost to that same simpletest-le-ssl.conf

Is there anything non-standard in how you are using sites-available and sites-enabled?

I ran this test with Certbot 5.2.2 on Ubuntu 22

2 Likes

Thank you for testing this and for the detailed explanation.

To clarify, I am also using Certbot 5.2.2 on Ubuntu 22.04.
Screenshots are attached below for each step

  1. Apache HTTP configuration
    I have a single Apache config file in sites-available that contains
    multiple HTTP VirtualHosts (port 80). This file is enabled using
    a2ensite, and the corresponding symlink exists in sites-enabled.

  2. First certificate issuance
    I run the following command for the first domain:

sudo certbot --apache --server https://one.digicert.com/mpki/api/v1/acme/v2/directory --eab-kid "id" --eab-hmac-key "key" --domain lighthouse1.sslasprin.com --agree-tos --non-interactive --email admin@lighthouse1.sslasprin.com --config-dir /etc/letsencrypt/lighthouse1.sslasprin.com

Result:

  • Certificate is issued successfully
  • A multiplevhost-le-ssl.conf file is created
  • Port 443 VirtualHost is configured correctly

Second certificate issuance
I then run the same command for the second domain:
sudo certbot --apache
--server https://one.digicert.com/mpki/api/v1/acme/v2/directory
--eab-kid "id"
--eab-hmac-key "key"
--domain lighthouse2.example.com
--agree-tos
--non-interactive
--email admin@lighthouse2.example.com
--config-dir /etc/letsencrypt/lighthouse2.example.com

Result:

  • Certificate issuance succeeds
  • However, the existing *-le-ssl.conf file from the first domain
    is modified/overwritten
  • Port 80 VirtualHost entries are also added into the same SSL file
  1. Observation
    Unlike your test, in my case Certbot reports that the *-le-ssl.conf
    file is created or modified directly under sites-enabled.
    This appears to cause Certbot to reuse or overwrite the existing
    SSL VirtualHost instead of isolating each domain.
    And aslo add the vhost of the second domain addtionally

My question:

  1. Has this behavior been tested or validated with non–Let’s Encrypt
    ACME servers such as DigiCert?
  2. Is the Apache plugin officially supported when using commercial
    ACME providers with EAB?
  3. Is there any Certbot version (older or newer) that is known to
    avoid this behavior with DigiCert ACME?

Thank you for your help — I appreciate any clarification.

Summary

This text will be hidden

1 Like

I made a mistake when I said my similar test worked fine. It did not. I can readily reproduce your problem.

I can't explain how I missed it. I'll blame my glasses :slight_smile: Sorry.

Definitely looks like a bug. I've asked the EFF's Certbot dev team to look at this thread. Normally bug reports are better reported at their github. But, seems a waste to have to repeat this thread there.

My initial SimpleTest.conf file had these two port 80 VHosts

<VirtualHost *:80>
    ServerName test1.example.com
    DocumentRoot /var/mike-notauth
</VirtualHost>
<VirtualHost *:80>
    ServerName test2.example.com
    DocumentRoot /var/mike-notauth
</VirtualHost>

After running these two commands

sudo certbot --apache -d test1.example.com
sudo certbot --apache -d test2.example.com

The conf file became this. Note the incorrect RewriteCond statements in the second VHost. That wouldn't interfere with its operation. It confirms the port 443 le-ssl.conf corruption isn't the only problem.

Note that after the first command all looked fine. The second command corrupted the conf file.

<VirtualHost *:80>
    ServerName test2.example.com
    DocumentRoot /var/mike-notauth
RewriteEngine on
RewriteCond %{SERVER_NAME} =test1.example.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>

<VirtualHost *:80>
    ServerName test2.example.com
    DocumentRoot /var/mike-notauth
RewriteEngine on
RewriteCond %{SERVER_NAME} =test2.example.com [OR]
RewriteCond %{SERVER_NAME} =test1.example.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>

The resulting le-ssl.conf file after both commands is below. Not only does it have a faulty port 443 section but there is a port 80 VHost for test2 subdomain. The overall Apache config now has a duplicate port 80 VHost for test2 domain.

See that test2 has become a ServerAlias and its certificate is named. So, requests to test1 would fail for invalid certificate.

<IfModule mod_ssl.c>
<VirtualHost *:443>
  ServerName test1.example.com
  DocumentRoot /var/mike-notauth

Include /etc/letsencrypt/options-ssl-apache.conf
ServerAlias test2.example.com
SSLCertificateFile /etc/letsencrypt/live/test2.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/test2.example.com/privkey.pem
</VirtualHost>
</IfModule>

<IfModule mod_ssl.c>
<VirtualHost *:80>
    ServerName test2.example.com
    DocumentRoot /var/mike-notauth

</VirtualHost>
</IfModule>
1 Like

While we wait for other experts to post have you considered using Apache's built-in ACME Client mod_md?

You just define a few config lines right in the Apache config and mod_md takes care of the rest. No need for a stand-alone ACME Client.

See: mod_md - Apache HTTP Server Version 2.4

For "how to" and more detailed docs see the mod_md github: GitHub - icing/mod_md: Let's Encrypt (ACME) in Apache httpd

We recommend this regularly. Its cert status is integrated into standard Apache management (see mod_status).

2 Likes

Thank you for confirming and reproducing the issue. I appreciate the detailed investigation.

I just wanted to clarify two points:

  1. I intend to continue using Certbot with the DigiCert ACME endpoint (using EAB) for certificate issuance and installation. I do not want to switch to alternatives like mod_md at this time.

  2. I noticed that a very similar issue was reported on the Let’s Encrypt community forum over five years ago:
    Certbot on Apache - Unable to Parse Multiple VHOSTS in one File - #7 by TDRogers

Since that thread was from around 2017 and the behavior is still present in Certbot 5.x, I wanted to ask:

  • Is this a known limitation that is unlikely to be resolved soon?
  • Is there any plan to address this limitation in a future Certbot release?
  • If it will take significant time to fix, is there an expected timeline or milestone?

Thank you again for your help and clarification.

Those are excellent questions for the EFF team who develop Certbot. Hopefully they post here and will let us know.

1 Like

Thank you for the clarification on Apache.

I did a similar test with Nginx and noticed a different behavior. Here’s what I observed:

I have a multi-domain Nginx configuration in a single file:

# -------- Domain 1 (HTTPS) --------
server {
    listen 443 ssl;
    server_name nginxcertbot1.sslasprin.com;

    ssl_certificate /etc/letsencrypt/live/nginxcertbot1.sslasprin.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/nginxcertbot1.sslasprin.com/privkey.pem;

    root /var/www/nginxcertbot1;
}

# -------- Domain 2 (HTTPS) --------
server {
    listen 443 ssl;
    server_name nginxcertbot2.sslasprin.com;

    ssl_certificate /etc/letsencrypt/live/nginxcertbot2.sslasprin.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/nginxcertbot2.sslasprin.com/privkey.pem;

    root /var/www/nginxcertbot2;
}

When I run Certbot for the first domain:

sudo certbot --nginx --server https://one.digicert.com/mpki/api/v1/acme/v2/directory \
--eab-kid "id" --eab-hmac-key "key" \
--domain nginxcertbot1.sslasprin.com \
--agree-tos --non-interactive \
--email admin@nginxcertbot1.sslasprin.com \
--config-dir /etc/letsencrypt/nginxcertbot1.sslasprin.com

it adds the 443 block to the same multi-domain 80 file.

Running Certbot again for the second domain:

sudo certbot --nginx --server https://one.digicert.com/mpki/api/v1/acme/v2/directory \
--eab-kid "id" --eab-hmac-key "key" \
--domain nginxcertbot2.sslasprin.com \
--agree-tos --non-interactive \
--email admin@nginxcertbot2.sslasprin.com \
--config-dir /etc/letsencrypt/nginxcertbot2.sslasprin.com

creates another 443 block in the same configuration file — it does not overwrite the first certificate.

:white_check_mark: This means Nginx + Certbot handles multiple HTTPS vhosts in a single file cleanly.

My question: is there a way to achieve similar behavior with Apache, so multiple HTTPS <VirtualHost> blocks can coexist in one file without Certbot creating separate conf files and overwriting certificates?

Thanks!

I would like to add one more observation based on my testing with Apache httpd on Amazon Linux.

On Amazon Linux (httpd), Certbot behaves as expected:

  • Certbot creates a separate *-le.conf file under conf.d
  • For each domain, it creates a separate <VirtualHost *:443> block
  • Existing SSL vhosts are not overwritten
  • Multiple HTTPS vhosts can coexist cleanly in the same file or in separate files

In contrast, on Ubuntu (apache2), Certbot creates a new site configuration file under sites-available and reuses or overwrites the SSL vhost instead of adding a new <VirtualHost *:443> block.

This confirms that:

  • Certbot works correctly with httpd
  • The different behavior appears to be related to Apache packaging and Certbot’s Apache plugin behavior on Debian/Ubuntu, not a general Certbot issue

I wanted to share this comparison to help clarify that the issue is environment-specific (Ubuntu apache2 vs RHEL/Amazon Linux httpd).

hey all, sorry to hear about this issue, and thanks for the super detailed bug report. just wanted to update you that the certbot team is looking into this now! we'll probably update the github issue ([Bug]: Certbot Apache plugin corrupts configs when multiple :80 VirtualHosts are in one file (SSL vhosts overwritten) · Issue #10524 · certbot/certbot · GitHub) as we're working on it, and then once it's resolved i'll update this thread.

3 Likes

Thanks for the update and for looking into this issue.

Please keep us informed via the GitHub issue as progress is made. Could you also let us know whether this fix will be included in a new Certbot release, and if possible, provide an estimated timeline for when the updated version might be available?

We’ll continue using a workaround in the meantime and are happy to share any additional logs or reproduction details if needed.

Thank you again for your support and prompt response.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.