Apache
References
- Apache Documentation (a bit fuzzy...)
- Apache HTTP Server Wiki (much clearer but incomplete)
How-to
Restart, reload, check configuration
service apache2 reload # reload apache configuration
service apache2 restart # restart apache
On Debian, with systemd:
apachectl configtest # Check apache configuration
systemctl restart apache2.service # Restart apache
systemctl start apache2.service # Start apache
systemctl stop apache2.service # Stop apache
Add new modules
a2enmod ssl # Enable SSL module (for https:// support)
a2enmod rewrite # Enable rewrite module (for URL rewrite)
a2enmod include # Enable include module (for server side include)
Create and enable new (virtual) hosts
Ideally, create your new (virtual) host as a .conf file in /etc/apache2/sites-available. For instance:
vi /etc/apache2/sites-available/www.myhost.org.conf
Example of host file:
<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerAdmin webmaster@localhost
ServerName www.immie.org
DocumentRoot /var/www/html
<Location />
AuthType Basic
AuthName "www.immie.org"
AuthUserFile /etc/apache2/www.immie.org.ssl.passwd
Require valid-user
</Location>
SSLEngine on
SSLCertificateFile /etc/ssl/certs/ssl-cert-www.immie.org.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-www.immie.org.key
</VirtualHost>
</IfModule>
Now enable the virtual host and reload apache configuration:
a2ensite www.myhost.org
service apache2 reload
Using multiple ssl certificates with one IP address
This only works with browser that support the TLS extension Server Name Indication (SNI) [1].
To have several server names on same IP address, each with their own certificate:
# NameVirtualHost *:443 # This directive is deprecated
# This is the configuration when going to https://www.yoursite.com
<VirtualHost *:443>
ServerName www.yoursite.com
DocumentRoot /var/www/site
SSLEngine on
SSLCertificateFile /path/to/www_yoursite_com.crt
SSLCertificateKeyFile /path/to/www_yoursite_com.key
SSLCertificateChainFile /path/to/DigiCertCA.crt
</VirtualHost>
# This is the configuration when going to https://www.yoursite2.com
<VirtualHost *:443>
ServerName www.yoursite2.com
DocumentRoot /var/www/site2
SSLEngine on
SSLCertificateFile /path/to/www_yoursite2_com.crt
SSLCertificateKeyFile /path/to/www_yoursite2_com.key
SSLCertificateChainFile /path/to/DigiCertCA.crt
</VirtualHost>
To have several server names on same IP address but with the same certificate. All servers must refer to the same IP address in the VirtualHost IP address section.
# This is the configuration when going to https://www.domain.com
<VirtualHost 192.168.1.1:443> # All configuration must refer to same IP address
ServerName www.domain.com
DocumentRoot /var/www/
SSLEngine on
SSLCertificateFile /path/to/your_domain_name.crt
SSLCertificateKeyFile /path/to/your_private.key
SSLCertificateChainFile /path/to/DigiCertCA.crt
</VirtualHost>
# This is the configuration when going to https://site2.domain.com
<VirtualHost 192.168.1.1:443> # All configuration must refer to same IP address
ServerName site2.domain.com
DocumentRoot /var/www/site2
SSLEngine on
SSLCertificateFile /path/to/your_domain_name.crt
SSLCertificateKeyFile /path/to/your_private.key
SSLCertificateChainFile /path/to/DigiCertCA.crt
</VirtualHost>
Redirect root to a subfolder
We use the RedirectMatch
directive
# Redirect / to /owncloud/
RedirectMatch "^/$" "/owncloud/"
Then reload the A2 configuration
service apache2 reload
❗ | Don't use Redirect because it performs prefix matching, and would produce infinite recursion. Also, if using 301 (permament redirect), the browser will cache the redirect, which will make TROUBLESHOOTING harder if the redirect was badly written. Changing the redirect afterwards would have no effect unless emptying the browser cache!
|
Return 404 for unknown domains
We need to add a catch-all configuration that will redirect to 404 for all domains:
# File: /etc/apache2/sites-available/999-default.conf
#
# We default to 404 for unknown domains.
# Make sure this configuration is loaded last
<VirtualHost *:80>
ServerAlias *
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
Redirect 404 /
</VirtualHost>
<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerAlias *
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
Redirect 404 /
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/noekeon.org/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/noekeon.org/privkey.pem
</VirtualHost>
</IfModule>
Note that Apache2 processes configuration file in alphabetical order, so make sure to rename other site configuration as 000-domain1.conf, 001-domain2.conf, etc.
Parsing configuration sections
See Official Apache documentation - sections for details.
- Difference between
and
[2]
Directory
directive works only for filesystem objects (e.g. /var/www/mypage, C:\www\mypage),- while
Location
directive works only for URLs.
- So
Location
overridesFiles
, - which overrides
DirectoryMatch
, with paths matchingDirectory
at the lowest priority. - The last one wins for conflicting directives.
- ... except for
ProxyPass
, which appears to work in the other direction.
Enabling .htaccess files
Support for file .htaccess must be enabled in the site configuration (/etc/apache2/sites-available). To enable all settings:
DocumentRoot /var/www/example.com
<Directory /var/www/example.com>
AllowOverride All
</Directory>
In case the .htaccess files are ignored (see [5]):
Stop! Don't use htaccess files for mod_rewrite unless you have no other choice. Doing so is slow and confusing.
- Put a nonsense line (such as Wooga) in your htaccess file and try the request again. If you don't see a 500 Internal Server Error message, your htaccess file is being ignored altogether. The solution is to set both AllowOverride FileInfo and Options FollowSymlinks in httpd.conf (on Ubuntu, check apache2/sites-enabled/000-default, or add your own config file in apache2/conf.d) for the directory in question.
- If you think your rules look ok but you still see a 500 Internal Server Error message, make sure mod_rewrite is loaded in the server.
- If you have ensured that mod_rewrite is loaded, and that RewriteRule is enabled for htaccess files, it could be that your rules are looping.
- If none of the above steps help, try a very simple rewrite to check if the module is enabled. For example:
DocumentRoot /var/www
<Directory /var/www> # MUST BE DIRECTORY WHERE .htaccess is
Options FollowSymLinks
AllowOverride FileInfo # Must *NOT* be ''none''
</Directory>
RewriteEngine On
# Redirect all requests to example.com
RewriteRule ^ http://example.com/
Rewrite Rules
- References: [6], [7]
- Rewrite rules are either defined in virtual host configuration (i.e. httpd.conf or similar) or in the .htaccess file, per directory (but this is discouraged — I don't know why exactly, slower...}}
Enable Rewrite module
Simply type:
a2enmod rewrite
service apache2 restart
Why they can't simply say so in apache doc is just a mistery.
Frequent errors
- Make sure that all files in your /var/www (or any other relevant directory) are owned by www:www-data (if not, rule conditions like
RewriteCond %{REQUEST_FILENAME} -f
may fail!)
sudo chown -R www:www-data /var/www
- If you changed apache config, make sure that you restarted the server
sudo /etc/init.d/apache2 restart
- If you use mod_rewrite in .htaccess files, make sure that these files are indeed read by Apache (see section above).
- Enable rewrite log to ease debugging. Add a file /etc/apache2/conf.d/rewritelog.conf:
RewriteLog "/var/log/apache2/rewrite.log"
RewriteLogLevel 8 # Max 9
Some example of rewrite rules
See [8] for more examples, and what-not.
# Permanent redirect from HTTP to HTTPS
#
# Note that this also redirect http://domain.com/foo to https://domain.com/foo.
<VirtualHost *:80>
ServerName domain.com
ServerAlias www.domain.com
Redirect permanent / https://domain.com/
</VirtualHost>
# (longer) alternative solution using RewriteRule
<VirtualHost *:80>
ServerName domain.com
RewriteEngine on
RewriteCond %{SERVER_NAME} =domain.com [OR]
RewriteCond %{SERVER_NAME} =www.domain.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
Basic .htaccess template
Basic template for a .htaccess file:
<IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L] </IfModule>
Rewrite URL for missing resource
From [9], rewrite URL for missing resource.
# For each web request (file or directory) that doesn't start with /en-US/,
# serve up the original resource if it exists, otherwise serve up the /en-US/ version.
RewriteCond $0 !^en-US/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .+ en-US/$0 [L]
Rewrite URL for missing resources (advanced)
# Try to replace query for non-existing images to white/black images
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -f # true if file exists
RewriteRule (.*) - [L] # Applied if condition above is true, [L] means LAST rule
# 10-0- up to 10-511-
RewriteCond %{REQUEST_URI} /10-[0-9]+-([0-9]|[1-9][0-9]|[1-4][0-9][0-9]|50[0-9]|51[0-1])\.png
RewriteRule (.*) white.png [L]
# 9-0- up to 9-255-
RewriteCond %{REQUEST_URI} /9-[0-9]+-([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.png
RewriteRule (.*) white.png [L]
# 8-0- up to 8-127-
RewriteCond %{REQUEST_URI} /8-[0-9]+-([0-9]|[1-9][0-9]|1[01][0-9]|12[0-7])\.png
RewriteRule (.*) white.png [L]
# 7-0- up to 7-63-
RewriteCond %{REQUEST_URI} /7-[0-9]+-([0-9]|[0-5][0-9]|6[0123])\.png
RewriteRule (.*) white.png [L]
# 6-0- up to 6-31-
RewriteCond %{REQUEST_URI} /6-[0-9]+-([0-9]|[0-2][0-9]|3[01])\.png
RewriteRule (.*) white.png [L]
# 5-0- up to 5-15-
RewriteCond %{REQUEST_URI} /5-[0-9]+-([0-9]|1[0-5])\.png
RewriteRule (.*) white.png [L]
# 4-0- up to 4-7-
RewriteCond %{REQUEST_URI} /4-[0-9]+-([0-7])\.png
RewriteRule (.*) white.png [L]
# 3-0- up to 3-3-
RewriteCond %{REQUEST_URI} /3-[0-9]+-([0-3])\.png
RewriteRule (.*) white.png [L]
# 2-0- up to 2-1-
RewriteCond %{REQUEST_URI} /2-[0-9]+-([0-1])\.png
RewriteRule (.*) white.png [L]
# 1-0- up to 1-1-
RewriteCond %{REQUEST_URI} /1-[0-9]+-0\.png
RewriteRule (.*) white.png [L]
RewriteRule (.*) black.png [L]
Include module
The include module enables server side includes (SSI). To enable the module:
sudo a2enmod include
sudo service apache2 restart
Then edit the site configuration to enable SSI [10]:
--- a/apache2/sites-available/default-ssl.conf
+++ b/apache2/sites-available/default-ssl.conf
@@ -17,6 +17,8 @@
</Location>
<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
+ Options +Includes
+ XBitHack Full
AllowOverride FileInfo
Order allow,deny
allow from all
Editing .htaccess in local directory is also possible but must be enabled. See Apache website for more detailed information.
Tags
DirectoryIndex
- Use DirectoryIndex to change list of default name of index file while browsing directory
DirectoryIndex index.php index.html # Will serve php version first
DirectoryIndex mycustomindex.html # To point to specific file when browsing directory (no directory listing)
SSL and HTTPS
Install
From [11]. Assumptions:
- Already a website present at /var/www
- Package ssl-cert installed (that create snakeoil (i.e. self-signed) certificates in /etc/ssl)
# Load and enable SSL module
sudo a2enmod ssl
sudo /etc/init.d/apache2 force-reload
# Edit file
sudo vim /etc/apache2/sites-available/default-ssl
# ... and change lines as follows (for key/cert we use the snakeoil ones):
# SSLEngine on
# SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire
#Enable the default SSL site:
sudo a2ensite default-ssl
# Tell Apache to reload its configuration:
sudo /etc/init.d/apache2 reload # or sudo service apache2 reload
To also add user authentication, add the following lines to either
- File .htaccess in website directory
- Section
<Location />
in /etc/apache2/sites-available/default-ssl:
- Do not use a Directory tag, it does not work [12], [13].
- Also, make suure that password file is not in the html subtree (i.e. can't be downloaded by clients).
<Location "/">
AuthType Basic
AuthName "default"
AuthUserFile /var/www/etc/filename.ssl.passwd
Require valid-user
</Location>
Then create the password file with the command htpasswd
htpasswd -c -s /var/www/etc/filename.ssl.passwd username # set password, using SHA-1
sudo chown www-data:www-data /var/www/etc/filename.ssl.passwd # set permission (or get 500 - Internal server error)
Finally, reload apache2:
sudo /etc/init.d/apache2 reload
In case of problem, check log file /var/log/apache2/error.log.
Parse and generate new self-signed certificates
References:
- On Debian
- General
- Generate the certificate [14], and change permission
sudo openssl req -x509 -nodes -days 3652 -newkey rsa:2048 -keyout ssl-cert-myserver.key -out ssl-cert-myserver.pem
sudo chown root:ssl-cert ssl-cert-myserver.key
sudo chmod 640 ssl-cert-myserver.key
sudo mv ssl-cert-myserver.key /etc/ssl/private
sudo mv ssl-cert-myserver.pem /etc/ssl/certs
- Edit SSL config for Apache (typically file /etc/apache2/sites-available/default-ssl.conf):
<VirtualHost _default_:443>
DocumentRoot /var/www/website
ServerName www.yourdomain.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/ssl-cert-myserver.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-myserver.key
</VirtualHost>
- Restart Apache:
sudo service apache2 restart # OR /usr/local/apache/bin/apachectl restart
The server private key is located at /etc/ssl/private/ssl-cert-snakeoil.key and the self-signed certificate at /etc/ssl/certs/ssl-cert-snakeoil.key.
To parse the current private key:
sudo openssl rsa -noout -text -in /etc/ssl/private/ssl-cert-snakeoil.key # Show coefficient of private key
sudo openssl rsa -noout -modulus -in /etc/ssl/private/ssl-cert-snakeoil.key # Show private key modulus
To parse the current self-signed certificate:
openssl asn1parse -in /etc/ssl/certs/ssl-cert-snakeoil.pem # Show certificate fields
This produces for instance:
openssl asn1parse -in /etc/ssl/certs/ssl-cert-snakeoil.pem
0:d=0 hl=4 l= 698 cons: SEQUENCE
4:d=1 hl=4 l= 418 cons: SEQUENCE
8:d=2 hl=2 l= 3 cons: cont [ 0 ]
10:d=3 hl=2 l= 1 prim: INTEGER :02
13:d=2 hl=2 l= 9 prim: INTEGER :C5E28AFC1DC8799C
24:d=2 hl=2 l= 13 cons: SEQUENCE
26:d=3 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption
37:d=3 hl=2 l= 0 prim: NULL
39:d=2 hl=2 l= 21 cons: SEQUENCE
41:d=3 hl=2 l= 19 cons: SET
43:d=4 hl=2 l= 17 cons: SEQUENCE
45:d=5 hl=2 l= 3 prim: OBJECT :commonName
50:d=5 hl=2 l= 10 prim: PRINTABLESTRING :zavcxv0035
62:d=2 hl=2 l= 30 cons: SEQUENCE
64:d=3 hl=2 l= 13 prim: UTCTIME :150605232158Z
79:d=3 hl=2 l= 13 prim: UTCTIME :250602232158Z
94:d=2 hl=2 l= 21 cons: SEQUENCE
96:d=3 hl=2 l= 19 cons: SET
98:d=4 hl=2 l= 17 cons: SEQUENCE
100:d=5 hl=2 l= 3 prim: OBJECT :commonName
105:d=5 hl=2 l= 10 prim: PRINTABLESTRING :zavcxv0035
117:d=2 hl=4 l= 290 cons: SEQUENCE
121:d=3 hl=2 l= 13 cons: SEQUENCE
123:d=4 hl=2 l= 9 prim: OBJECT :rsaEncryption
134:d=4 hl=2 l= 0 prim: NULL
136:d=3 hl=4 l= 271 prim: BIT STRING
411:d=2 hl=2 l= 13 cons: cont [ 3 ]
413:d=3 hl=2 l= 11 cons: SEQUENCE
415:d=4 hl=2 l= 9 cons: SEQUENCE
417:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Basic Constraints
422:d=5 hl=2 l= 2 prim: OCTET STRING [HEX DUMP]:3000
426:d=1 hl=2 l= 13 cons: SEQUENCE
428:d=2 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption
439:d=2 hl=2 l= 0 prim: NULL
441:d=1 hl=4 l= 257 prim: BIT STRING
- The field UTCTIME is coded as
YYMMDDHHmmssZ
.
To generate a new key and certificate:
openssl req -x509 -nodes -days 3652 -newkey rsa:2048 -keyout ssl-cert-$HOSTNAME.key -out ssl-cert-$HOSTNAME.pem
Typically you can leave all request fields, except the Common Name which should be set to some name, or server FQDN:
Generating a 2048 bit RSA private key
..............................................................+++
..................................+++
unable to write 'random state'
writing new private key to 'ssl-cert-zavcxl0005.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:.
State or Province Name (full name) [Some-State]:.
Locality Name (eg, city) []:.
Organization Name (eg, company) [Internet Widgits Pty Ltd]:.
Organizational Unit Name (eg, section) []:.
Common Name (e.g. server FQDN or YOUR name) []:zavcxl0005.zav.st.com
Email Address []:.
Then install the key and certificate and update /etc/apache2/default-ssl.conf:
sudo mv ssl-cert-$HOSTNAME.pem /etc/ssl/certs
sudo chmod 640 ssl-cert-$HOSTNAME.key
sudo chown root:ssl-cert ssl-cert-$HOSTNAME.key
sudo mv ssl-cert-$HOSTNAME.key /etc/ssl/private
Edit /etc/apache2/sites-available/default-ssl.conf (replacing myhostname
as necessary):
- SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
- SSLCertificateKeyFile /etc/ssl/private/ssl-cert-myhostname.key
+ SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
+ SSLCertificateKeyFile /etc/ssl/private/ssl-cert-myhostname.key
Generate a new Certificate Authority (CA) and new server SSL certificate
We use script CA.pl from openssl to create a custom CA and generate a new server SSL certificate. By installing the new CA certificate in their browser, users will not get the annoying message about untrusted certificate.
cd /root/etc
cp /usr/lib/ssl/misc/CA.pl .
# Enforce 3650 days validity (10 years)
sed -ri 's/365/3650/; s/1095/3650/' CA.pl
# Create the CA key in demoCA/
./CA.pl -newca # Write down CA key password
/usr/lib/ssl/misc/c_info demoCA/cacert.pem
openssl x509 -text -fingerprint -sha1 -in demoCA/cacert.pem -out demoCA/cacert.wiki.protonworld.com.crt
# Create the server private key + signing request
./CA.pl -newreq-nodes
# Generate the server certificate
./CA.pl -sign # Requires CA key password
# Install the server certificates
mv newcert.pem /etc/ssl/certs/ssl-cert-wiki.protonworld.com.pem
mv newkey.pem /etc/ssl/private/ssl-cert-wiki.protonworld.com.key
service apache2 restart
Now, users must install the CA certificate /root/etc/demoCA/cacert.wiki.protonworld.com.crt into their browser trusted CA. For internet explorer,
- Double click on certificate file.
- Click Install Certificate..., then Next.
- Select Place all certificates in the following store, and click Browse....
- Select Trusted Root Certification Authorities, and click OK.
- Click Next and Finish. The popup window will report a thumbprint. Check the reference value and click Yes if identical.
Tips
- Redirecting HTTP to HTTPS
- Edit the configuration file as follows [15]:
NameVirtualHost *:80 <VirtualHost *:80> ServerName mysite.example.com DocumentRoot /usr/local/apache2/htdocs Redirect permanent / https://mysite.example.com/ </VirtualHost> <VirtualHost _default_:443> ServerName mysite.example.com DocumentRoot /usr/local/apache2/htdocs SSLEngine On # etc... </VirtualHost>
- Then restart apache:
sudo service apache2 restart
Another solution is a rewrite rule in httpd.conf [16]:
RewriteEngine On RewriteCond %{HTTPS} off RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
Troubleshooting
- Invalid command 'ProxyRequests'
Invalid command 'ProxyRequests', perhaps misspelled or defined by a module not included in the server configuration
- Solution is to enable module proxy:
sudo a2enmod proxy
sudo service apache2 restart
- Invalid command 'RequestHeader'
Invalid command 'RequestHeader', perhaps misspelled or defined by a module not included in the server configuration
- Solution is to enable module headers:
sudo a2enmod headers
sudo service apache2 restart
- No protocol handler was valid for the URL /...
[proxy:warn] ... No protocol handler was valid for the URL /SOGo. If you are using a DSO version of mod_proxy, make sure the proxy submodules are included in the configuration using LoadModule
- Solution is to enable module proxy_http:
a2enmod proxy_http
a2enmod proxy
sudo service apache2 restart
- .htaccess - DirectoryIndex not allowed here
/.../.htaccess: DirectoryIndex not allowed here
- Solution is to grant
AllowOverride Indexes
in the server configuration [17]. - Another solution is to use a rewrite rule:
RewriteRule ^$ index.php
- SSL server name mismatch
server certificate does NOT include an ID which matches the server name
- Make sure the server name and certificate subject matches. Check
ServerName
adSSLCertificateFile
in Apache configuration:
- Make sure the server name and certificate subject matches. Check
<VirtualHost *:443>
ServerName miki.immie.org
SSLCertificateFile /etc/ssl/certs/ssl-cert-miki.immie.org.pem
- Get the subject name from given certificate:
openssl x509 -in /etc/ssl/certs/ssl-cert-miki.immie.org.pem -noout -subject
# subject= /C=BE/ST=Brussels/L=Brussels/O=immie.org/CN=miki.immie.org/emailAddress=miki@immie.org
- Make sure that the public certificate and private key matches:
<VirtualHost *:443>
SSLCertificateFile /etc/ssl/certs/ssl-cert-miki.immie.org.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-miki.immie.org.key
- Header not allowed here
- RewriteEngine not allowed here
Check that you have the following line in apache config. Also check that there are not multiple AllowOverride
directives because only the last directive is taken into account.
AllowOverride FileInfo
- .../.htaccess
- Options not allowed here, referer...
I have in .htaccess:
Options +FollowSymLinks
The solution is to change the AllowOverride
in site configuration file [18]:
- AllowOverride FileInfo Indexes
+ AllowOverride all