# Fix Apache VirtualHost Not Matching Correct ServerName
You have multiple VirtualHost blocks configured in Apache, but requests to api.example.com are being served by the www.example.com VirtualHost. The wrong SSL certificate is returned, or the wrong document root is used.
How Apache Selects a VirtualHost
Apache selects VirtualHosts using this order:
- 1.IP address and port: Apache first matches the incoming request's destination IP and port against
VirtualHostdeclarations - 2.ServerName and ServerAlias: Within matching IP:port blocks, Apache matches the
Hostheader againstServerNameandServerAlias - 3.First VirtualHost as default: If no
ServerNamematches, Apache uses the first VirtualHost defined for that IP:port
Diagnosing the Mismatch
List all configured VirtualHosts:
apache2ctl -S
# or
httpd -SThis shows the VirtualHost configuration as Apache understands it:
VirtualHost configuration:
*:443 is a NameVirtualHost
default server www.example.com (/etc/apache2/sites-enabled/000-default.conf:1)
port 443 namevhost www.example.com (/etc/apache2/sites-enabled/000-default.conf:1)
port 443 namevhost api.example.com (/etc/apache2/sites-enabled/api.conf:1)
port 443 namevhost blog.example.com (/etc/apache2/sites-enabled/blog.conf:1)
*:80 is a NameVirtualHost
default server www.example.com (/etc/apache2/sites-enabled/000-default.conf:10)
port 80 namevhost www.example.com (/etc/apache2/sites-enabled/000-default.conf:10)- 1.If a request to
api.example.comis being served bywww.example.com, check: - 2.Is
api.example.comlisted in the output for the correct port? - 3.Is the
ServerNamedirective spelled correctly?
Common Causes
1. Wrong Port in VirtualHost
```apache # WRONG: api.example.com is on port 80, but request comes on port 443 <VirtualHost *:80> ServerName api.example.com DocumentRoot /var/www/api </VirtualHost>
# CORRECT: Match the incoming port <VirtualHost *:443> ServerName api.example.com DocumentRoot /var/www/api # SSL configuration </VirtualHost> ```
2. Missing ServerName
<VirtualHost *:443>
# No ServerName -- this becomes the default for all unmatched requests on 443
DocumentRoot /var/www/default
</VirtualHost>Always include a ServerName directive. The VirtualHost without a ServerName becomes the catch-all default.
3. ServerAlias Typo
<VirtualHost *:443>
ServerName api.example.com
ServerAlias api.example.con # Typo: .con instead of .com
DocumentRoot /var/www/api
</VirtualHost>4. Configuration File Loading Order
Apache loads configuration files alphabetically within each configuration directory. The first VirtualHost for a given IP:port becomes the default:
ls -la /etc/apache2/sites-enabled/
# 000-default.conf loads before api.conf
# 000-default becomes the default serverThis means requests with unrecognized or missing Host headers go to 000-default. Ensure your default VirtualHost is intentional.
Testing VirtualHost Matching
```bash # Test which VirtualHost handles a specific host curl -sI -H "Host: api.example.com" http://127.0.0.1/ | grep -E "HTTP/|Server:"
# Use apache2ctl to check matching apache2ctl -S 2>&1 | grep "api.example.com" ```
SNI (Server Name Indication) for SSL
For HTTPS VirtualHosts, the client must support SNI to send the hostname before the TLS handshake. Test SNI:
echo | openssl s_client -connect example.com:443 -servername api.example.com 2>&1 | grep "subject="If the certificate returned does not match api.example.com, SNI is not working correctly. Ensure:
<VirtualHost *:443>
ServerName api.example.com
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/api.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/api.example.com/privkey.pem
</VirtualHost>Each VirtualHost must have its own SSL certificate. Apache uses the ServerName from the TLS ClientHello SNI extension to select the correct certificate.
The Catch-All VirtualHost
Create an intentional catch-all for unknown hosts:
```apache <VirtualHost *:80> ServerName _ DocumentRoot /var/www/catchall
# Return 444 for unknown hosts (Nginx-style, Apache equivalent) <Location "/"> RewriteEngine On RewriteRule ^ - [R=403,L] </Location> </VirtualHost> ```
Place this as the first VirtualHost file (e.g., 000-catchall.conf) so it becomes the default. This prevents accidental serving of content from other VirtualHosts for requests with missing or unrecognized Host headers.