Apache

From miki
(Redirected from Apache - .htaccess)
Jump to navigation Jump to search

References

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.
Order of matching [3], [4]
  • So Location overrides Files,
  • which overrides DirectoryMatch, with paths matching Directory 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.

  1. 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.
  2. DocumentRoot /var/www
    <Directory /var/www>                # MUST BE DIRECTORY WHERE .htaccess is
        Options FollowSymLinks
        AllowOverride FileInfo          # Must *NOT* be ''none''
    </Directory>
    
  3. 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.
  4. 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.
  5. If none of the above steps help, try a very simple rewrite to check if the module is enabled. For example:
  6. 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 ad SSLCertificateFile in Apache configuration:
<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