Apache VirtualHost
{: .warning } You CANNOT test or use https until you have a certificate! You will also need to configure ssl! http is required first, so you can configure ssl
DNS
A Record
I point something.com to x.x.x.x IP
CNAME record
I point my CNAME of www.something.com to something.com
Apache VirtualHost
Assume your site is called “somesite.com”.
Install Apache
sudo apt install apache2
Disable the Default Site
sudo a2dissite 000-default
Copy Default Site Config
sudo mv /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/somesite.com.conf
sudo vim /etc/apache2/sites-available/somesite.com.conf
Allow Ports you want
I want to enable port 80, 443 and 4000. 80 and 4000 will be over http, not encrypted.
vim /etc/apache2/ports.conf
Listen 0.0.0.0:80
Listen 0.0.0.0:4000
<IfModule ssl_module>
Listen 0.0.0.0:443
</IfModule>
<IfModule mod_gnutls.c>
Listen 0.0.0.0:443
</IfModule>
Create Directories and Logs
sudo mkdir /var/www/logs/
sudo mkdir /var/www/html/somesite.com/
sudo mkdir /var/www/html/grimoire.somesite.com/
sudo touch /var/www/logs/somesite.com.error.log
sudo touch /var/www/logs/somesite.com.access.log
Create port 80 Virtual Host
The *:port line says listen on any, and that port. I have specified ipv4 only in the ports.conf above.
#----------------------------------------
# somesite.com:80
#----------------------------------------
<VirtualHost *:80>
ServerAdmin admin@somesite.com
ServerName somesite.com
ServerAlias www.somesite.com
DirectoryIndex index.php index.html index.cgi
DocumentRoot /var/www/html/somesite.com
AddHandler cgi-script .cgi
<Location /cgi-bin>
Options +ExecCGI
</Location>
<Directory /var/www/html/somesite.com>
Order allow,deny
Allow from all
Require all granted
</Directory>
RewriteEngine On
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^www\. [NC]
RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [L,NE,R=301]
ErrorLog /var/www/logs/somesite.com.error.log
CustomLog /var/www/logs/somesite.com.access.log combined
</VirtualHost>
Create port 443 Virtual Host
I do not need to rewrite 443 to 443, but I still want www.something.com redirected to something.com
I also want to serve files that end in .cgi and make them executable. This might be outdated, but still works. CGI is another topic on security, but you can use any backend script to respond as a web app. There are more modern ways now.
#----------------------------------------
# somesite.com:443
#----------------------------------------
<VirtualHost *:443>
ServerAdmin admin@somesite.com
ServerName somesite.com
ServerAlias www.somesite.com
DirectoryIndex index.php index.html index.cgi
DocumentRoot /var/www/html/somesite.com
ScriptAlias "/cgi-bin/" "/var/www/html/somesite.com/cgi-bin/"
<Location /cgi-bin>
AddHandler cgi-script .cgi
Options +ExecCGI
</Location>
<Directory /var/www/html/somesite.com>
Order allow,deny
Allow from all
Require all granted
</Directory>
RewriteEngine On
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^www\. [NC]
RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [L,NE,R=301]
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/somesite.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/somesite.com/privkey.pem
ErrorLog /var/www/logs/somesite.com.error.log
CustomLog /var/www/logs/somesite.com.access.log combined
</VirtualHost>
Create the Subdomain and Port
I have a private wiki that does not use SSL at http://subdomain.something.com:4000
#----------------------------------------
# grimoire.somesite.com:4000
#----------------------------------------
<VirtualHost *:4000>
ServerAdmin admin@somesite.com
ServerName grimoire.somesite.com
#ServerAlias grimoire.somesite.com
DirectoryIndex index.html
DocumentRoot /var/www/html/grimoire.somesite.com
<Directory /var/www/html/grimoire.somesite.com>
Order allow,deny
Allow from all
Require all granted
</Directory>
ErrorLog /var/www/logs/grimoire.somesite.com.error.log
CustomLog /var/www/logs/grimoire.somesite.com.access.log combined
</VirtualHost>
Enable the Modules
We are using auth_digest, headers and rewrite so…
sudo a2enmod auth_digest headers rewrite
Enable the New Config
sudo a2ensite somesite.com
Test the Configs, Restart
{: .warning } Again, you cannot configure/test/restart until you complete SSL.
sudo apache2ctl -t
sudo systemctl restart apache2
sudo apache2ctl -S
Review Headers
(1:2311)# curl -sIv http://grimoire.somesite.com
* Rebuilt URL to: http://grimoire.somesite.com/
* Trying 162.243.46.68...
* TCP_NODELAY set
* Connected to grimoire.somesite.com (162.243.46.68) port 80 (#0)
> HEAD / HTTP/1.1
> Host: grimoire.somesite.com
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Date: Mon, 14 Aug 2023 12:22:02 GMT
Date: Mon, 14 Aug 2023 12:22:02 GMT
< Server: Apache
Server: Apache
< X-XSS-Protection: 1; mode=block
X-XSS-Protection: 1; mode=block
< x-Frame-Options: SAMEORIGIN
x-Frame-Options: SAMEORIGIN
< X-Content-Type-Options: nosniff
X-Content-Type-Options: nosniff
< Content-Security-Policy: default-src 'self'; font-src *;img-src * data:; script-src *; style-src *;
Content-Security-Policy: default-src 'self'; font-src *;img-src * data:; script-src *; style-src *;
< Referrer-Policy: strict-origin
Referrer-Policy: strict-origin
< Last-Modified: Sat, 11 Jan 2020 23:45:23 GMT
Last-Modified: Sat, 11 Jan 2020 23:45:23 GMT
< ETag: "157-59be5d603f2dd"
ETag: "157-59be5d603f2dd"
< Accept-Ranges: bytes
Accept-Ranges: bytes
< Content-Length: 343
Content-Length: 343
< Vary: Accept-Encoding
Vary: Accept-Encoding
< Content-Type: text/html
Content-Type: text/html
<
* Connection #0 to host grimoire.somesite.com left intact
Note, you CANNOT test or use https until you have a certificate!
(1:2312)# curl -sIv https://somesite.com
* Rebuilt URL to: https://somesite.com/
* Trying 162.243.46.68...
* TCP_NODELAY set
* Connected to somesite.com (162.243.46.68) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS Unknown, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Unknown (8):
* TLSv1.3 (IN), TLS Unknown, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS Unknown, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS Unknown, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Client hello (1):
* TLSv1.3 (OUT), TLS Unknown, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
* subject: CN=somesite.com
* start date: Jul 2 20:48:16 2023 GMT
* expire date: Sep 30 20:48:15 2023 GMT
* subjectAltName: host "somesite.com" matched cert's "somesite.com"
* issuer: C=US; O=Let's Encrypt; CN=R3
* SSL certificate verify ok.
* TLSv1.3 (OUT), TLS Unknown, Unknown (23):
> HEAD / HTTP/1.1
> Host: somesite.com
> User-Agent: curl/7.58.0
> Accept: */*
>
* TLSv1.3 (IN), TLS Unknown, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS Unknown, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS Unknown, Unknown (23):
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Date: Mon, 14 Aug 2023 12:22:27 GMT
Date: Mon, 14 Aug 2023 12:22:27 GMT
< Server: Apache
Server: Apache
< X-XSS-Protection: 1; mode=block
X-XSS-Protection: 1; mode=block
< x-Frame-Options: SAMEORIGIN
x-Frame-Options: SAMEORIGIN
< X-Content-Type-Options: nosniff
X-Content-Type-Options: nosniff
< Content-Security-Policy: default-src 'self'; font-src *;img-src * data:; script-src *; style-src *;
Content-Security-Policy: default-src 'self'; font-src *;img-src * data:; script-src *; style-src *;
< Referrer-Policy: strict-origin
Referrer-Policy: strict-origin
< Strict-Transport-Security: max-age=31536000; includeSubDomains
Strict-Transport-Security: max-age=31536000; includeSubDomains
< Link: <https://somesite.com/wp-json/>; rel="https://api.w.org/"
Link: <https://somesite.com/wp-json/>; rel="https://api.w.org/"
< Link: <https://somesite.com/>; rel=shortlink
Link: <https://somesite.com/>; rel=shortlink
< Content-Type: text/html; charset=UTF-8
Content-Type: text/html; charset=UTF-8
<
* Connection #0 to host somesite.com left intact