Setting up a basic web server

Peter Boy (pboy), Emmanuel Seyman (eseyman) Versio F38,F39 Last review: 2024-01-10
The recommended and fully supported Fedora Web server is Apache, named httpd in the distribution. It is a Fedora Server key functionality that is part of the services specified in the technical specification. This article is about setting up a basic server to serve static html pages. Further articles build on this and describe additional deployment configurations.

This article has not yet been finally reviewed. Some content may still be subject to change.

The Fedora Web server httpd is basically an Apache web server. This document covers in detail especially the setup and maintenance of certificates for secure, encrypted connections, which are standard today. It also covers various other configurations such as access restriction via authentication or WebDAV for uploading and modifying files. It also focuses on the delivery of static web pages. Further articles describe how httpd can be configured for other usage scenarios such as frontend for application servers, proxy for containers, integration of dynamic languages and other deployment options.

How it works

A Fedora Web Server installation stores the configuration in subdirectories of /etc/httpd. Only 2 subdirectories are relevant for the system administrator. The directory /etc/httpd/conf.d is the more important one and stores especially the configuration of the different web sites. This directory is where the system administrator works the most. The directory /etc/httpd/conf.modules.d contains the modules to be loaded dynamically and their configuration. Only very rarely does anything need to be changed here.

The data for the default web site (or "main" site) is by default located in /var/www, the html-files in its subdirectory html. Additional directories are provided for extended configurations, e.g. the cgi-bin subdirectory for storing (classic) CGI files.

This structure dates back to the early days of Linux systems, when the server hardware was capable to serve only one site (i.e. one Domain). As the hardware got more powerful, and became capable of serving more than one domain, "virtual servers" were added that provided additional domains – distinguished either by name (virtual named hosts) or by IP. This situation is still virulent in the term "main“ site or "main“ server.

All these distinctions have now been abandoned. Today, it is widespread for a server to host several domains as a matter of course. And it is now best practice to configure everything as a virtual host, even if a server only serves one domain.

In the Apache software, the "main" server is still present and serves as the default configuration of the most important parameters, which are inherited as default settings by the virtual host(s).

This development has consequences for the organization of data storage.

If you only serve one domain, you still could simply use that "main"" site directory and put files into the /var/www/html subdirectory and use it for your single virtual host. And this directory ensures, that the httpd server is fully functional out of the box for such a purpose. The mod_ssl module even provides a working https configuration based on an also provided self-signed certificate.

But in all cases you need a more appropriate alternative data organization. The first structuring level should be the domain, not the technology (like html and cgi-bin in the default).

One option is to use the /var/www/ directory to create domain-specific subdirectories and additional appropriate subdirectory within. The advantage of this procedure is that it uses many default httpd configurations, e.g. the SELinux labels. The disadvantage is that the default configuration of the distribution is modified. It is generally better to leave this untouched.

An alternative option, in many cases a better fit to FHS compliance is the /srv directory. Today you would have many Domains, which are served by one or more applications. According to the FHS, the /srv directory is the appropriate place for storing data. You create a domain-specific directory, e.g. example.com, and therein a htdocs subdirectory for static html files, a webapps subdirectory for your web applicaion, e.g Ruby on Rails, a mail subdirectory for a postfix/dovecot mail hub, etc.

In this guide we use the latter option. Therefore, we use directories like /srv/<DOMAINNAME>/ to store all data relevant to a domain, and /srv/<DOMAINMANE>/htdocs/ for static HTML pages. If you want to use the former option, you can replace /srv/ by /var/www/.

Storage preparation

Before installing the Software you have to prepare the storage. The Fedora Server Edition storage concept requires the creation of an LVM logical volume to hold the user data. There are at least two reasonable options:

  1. Create a logical volume of a suitable size and mount it on /srv (or /var/www).

  2. Create a logical volume Pool for thinly provisioned Volumes and therein one thin provisioned volume per domain.

The former is a good choice if there are many domains, each with a small amount of data, e.g. if the web server is mainly used as a front end for web application servers that manage their data in their own way, e.g. Apache Tomcat in /var/lib/tomcat/webapps or /var/lib/tomcats/<domain>/webapps or if they are stored in a database or a central GIT repository (Jamstack CMS).

The latter is a good choice if a large amount of data is generated in each domain (or most of them) because the data consists of extensive static HTML files, or an application server created directly in the http document root, usually with Ruby on Rails, for example, or a Jamstack based CMS with decentralized GIT repositories.

And of course you can also combine both.

The easiest way is to create the required volumes with Cockpit, the web-based server administration tool.

Installation

  1. Install the Apache httpd web server. Today, you will almost always need the modules for managing SSL connections and the module for domain monitoring. Everything now runs via https.

    […]$ sudo dnf install httpd mod_ssl mod_md
    […]$ sudo firewall-cmd --add-service=https --permanent
    […]$ sudo firewall-cmd --add-service=http  --permanent
    […]$ sudo firewall-cmd --reload
  2. Start the web server

    […]$ sudo systemctl start httpd
    […]$ sudo systemctl status httpd
    […]$ sudo systemctl enable httpd
    Created symlink /etc/systemd/system/multi-user.target.wants/httpd.service → /usr/lib/systemd/system/httpd.service.
  3. The Web server should already answer to requests and show the Fedora test page. Enter your server’s address into your browser’s address input field.

    Fedora test page
  4. If you plan to manage Let’s Encrypt certificates using certbot install

    […]$ sudo dnf install letsencrypt

Setup a web site

  1. Setup the web site Document Root directory

    As discussed above there are several options. In this example we use the /srv alternative and the website base name as base directory.

    […]$ sudo -i
    […]# mkdir -p  /srv/SITENAME/htdocs

    It you opt for thinly provisioned volumes use Cockpit to create the volume, format the filesystem and permanently mount it at the base location /srv/SITEMANE/. Cockpit performs all these steps, you just need to create the htdocs subdirectory.

    With /srv/ as the base directory you must adjust the SELinux labels.

    […]# /usr/sbin/semanage fcontext -a -t httpd_sys_content_t  -s system_u  "/srv/SITENAME/htdocs(/.*)?"
    […]# /sbin/restorecon -R -vF /srv/SITENAME/htdocs
    Relabeled /srv/SITENAME/htdocs from unconfined_u:object_r:var_t:s0 to system_u:object_r:httpd_sys_content_t:s0

    Create a very basic index page in your document root directory

    […]# vim /srv/SITENAME/htdocs/index.html
    <h1><center>It works!</center></h1>
  2. Configure a Virtual Host for the domain

    To make it easier for you we provide a reasonably detailed template. Download and save it into the directory /etc/httpd/conf.d/. Copy the template e.g. as zvhost-SITENAME.conf and adapt it to your requirements.

    It is good practice to follow a systematic naming convention. In the example, we use the prefix "vhost" to clearly distinguish the domain virtual host configurations from other configurations.

    The configuration files are read in alphabetically order. The order is significant. The first virtual host found, becomes the default host. It is always used if no suitable host name is found for a request (e.g. because the IP address is used).

    A website using the same name as the server’s hostname or its DNS entry, requires special measures. See section Troubleshooting.

    Edit the copied template file and adjust it as appropriate.

    […]$ sudo vi /etc/httpd/conf.d/zvhost-SITENAME.conf
    # Apache vhost configuration for a static html server.
    # It manages SSL connections including certificates.
    # Initially, a self-signed certificate is active.
    # Incoming http traffic is automatically redirected to https.
    # Version 2.1
    
    #==> To adjust in vi/vim copy and adjust to the vi command line:
    # : %s/SHORT_DESCR/real_short_descr/g    e.g. my-domain.org production server
    # : %s/FQN_NAME/your_domain/g            e.g. my-domain.org
    # : %s/BASE_NAME/your_shortname/g        e.g. my-domain
    # : %s/OPTIONAL_ALIAS/your_alias/g       e.g. www.my-domain.org
    # afterwards delete these lines
    
    
    # Certificates are managed by Apache md module.
    #==> To activate, remove the leading '#' character and comment out the
    # the default distribution provided certificates further down.
    #==> Adjust the mail address as appropriate!
    #MDContactEmail root@FQN_NAME
    #MDCertificateAgreement accepted
    #MDomain FQN_NAME
    
    <VirtualHost *:443>
    	# Secure virtual WEB host configuration for
    	# SHORT_DESCR
    
    	# The site can be accessed by https/ssl only. Without a valid certificate
    	# you have to use a self-signed certificate as a quick temporary fix.
    
    	ServerName      FQN_NAME
    	ServerAlias	OPTIONAL_ALIAS
    
        #==> Adjust the mail address as appropriate!
    	ServerAdmin     root@FQN_NAME
    
    	# ##########################################################################
    	# NOTE: We re-route everything from the insecure site to this secure site!
    	# ##########################################################################
    
    	# Optional: Ensure that all registered domain names are rewritten to the
    	# official base name
    	#RewriteEngine   On
    	#RewriteCond     %{HTTP_HOST}    !^FQN_NAME [NC]
    	#RewriteCond     %{HTTP_HOST}    !^$
    	#RewriteRule     ^(.*)$          https://FQN_NAME$1  [R=301,L]
    
    	# ====================================================================
    	# Certificates configuration
    	# ====================================================================
    	SSLEngine on
    	# We rely on Fedora's systemwide configuration of SSL security.
    
    	# By default, certificates are managed by Apache md module (see above)
    	# In this case, no certificates needs bo be configured here.
    	# Otherwise, insert proper certificate configuration.
    
    	# DEFAULT mod_ssl provided, needed for initial startup.
    	#==> Comment OUT when module md created a certificate or you use
    	# custom certificates.
    	SSLCertificateFile	/etc/pki/tls/certs/localhost.crt
    	SSLCertificateKeyFile	/etc/pki/tls/private/localhost.key
    
    	# LetsEncrypt certificates managed by certbot (NOT by module md!)
    	#SSLCertificateFile      /etc/letsencrypt/live/DOMAIN_NAME/cert.pem
    	#SSLCertificateKeyFile   /etc/letsencrypt/live/DOMAIN_NAME/privkey.pem
    	#SSLCertificateChainFile /etc/letsencrypt/live/DOMAIN_NAME/chain.pem
    
    	# ===============================================================
    	# Directory Locations
    	# ===============================================================
    	DirectoryIndex	index.html
    	DocumentRoot	/srv/BASE_NAME/htdocs
    	# Specific to default 2.4 configuration:
    	# Enable access to server-specific base file location
    	<Directory "/srv/BASE_NAME">
    		AllowOverride None
    		# Allow open access:
    		Require all granted
    	</Directory>
    	# Further relax access to the default document root
    	<Directory "/srv/BASE_NAME/htdocs">
    		#
    		# Possible values for the Options directive are "None", "All",
    		# or any combination of:
    		#   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
    		#
    		# Note that "MultiViews" must be named *explicitly* --- "Options All"
    		# doesn't give it to you.
    		#
    		# The Options directive is both complicated and important.  Please see
    		# http://httpd.apache.org/docs/2.4/mod/core.html#options
    		# for more information.
    		#
    		Options Indexes FollowSymLinks
    
    		#
    		# AllowOverride controls what directives may be placed in .htaccess files.
    		# It can be "All", "None", or any combination of the keywords:
    		#   Options FileInfo AuthConfig Limit
    		#
    		AllowOverride None
    
    		#
    		# Controls who can get stuff from this server:
    		# Allow open access:
    		Require all granted
    
    	</Directory>
    
    
    	# ===============================================================
    	# Optional: Protect access to start page (and subsequent pages)
    	#==>        Ensure you created the additional auth.d directory
    	#           including SELinux labels
    	# ===============================================================
    	#<Location />
    	#	AuthType Basic
    	#	AuthName "Access start page"
    	#	AuthUserFile /srv/BASE_NAME/auth.d/htuser
    	#	Require valid-user
    	#</Location>
    
    
    	# ===============================================================
    	# Optional: Configure webDAV access
    	#==>        Ensure you created the additional davlock directory
    	#           including SELinux labels
    	# ===============================================================
    	#DavLockDB /srv/SERVER_SHORT_NAME/davlock/dav_lock_db
    
    	#<Location /dav>
    	#	DAV On
    	#	ForceType text/plain
    
    	#	Order Allow,Deny
    	#	Allow from all
    	#	Options all
    	#	DirectoryIndex none
    
    		# Optional: Protect basic dav page (and all subsequent page)
    		#AuthType Basic
    		#AuthName "Application Server WebDAV access"
    		#AuthUserFile /srv/SERVER_SHORT_NAME/auth.d/htdavuser
    		#Require valid-user
    	#</Location>
    
    
    	# ===============================================================
    	# Logging configuration
    	# ===============================================================
    	# Use separate log files for the SSL virtual host; note that LogLevel
    	# is not inherited from httpd.conf.
    	# NOTE: fail2ban searches for ~/logs/*access_log and  ~/logs/*error_log
    	#       to access log files to watch and analyze!
    	ErrorLog        logs/BASE_NAME-ssl_error_log
    	CustomLog       logs/BASE_NAME-ssl_access_log combined
    	LogLevel warn
    
    </VirtualHost>
    
    <VirtualHost *:80>
    	# INSECURE virtual WEB host configuration for
    	# SHORT_DESCR
    
    	# NOTE: Everything from the insecure port 80 is redirected to this instance'
    	#       SECURE site
    
    	ServerName      FQN_NAME
    	ServerAlias	OPTIONAL_ALIAS
    
    	ServerAdmin     root@FQN_NAME
    
    	# ##########################################################################
    	# NOTE: We re-route everything to the secure site!
    	#       We retain all aliase names for now.
    	#       There is no need for an exception for Let's Encrypt anymore.
    	#       Version 2.x can deal with self-signed certificates and https
    	# ##########################################################################
    	RewriteEngine   On
    	RewriteRule	(.*)	https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
    
    
    	# ===============================================================
    	# Logging configuration
    	# ===============================================================
    	# Use separate log files for the SSL virtual host; note that LogLevel
    	# is not inherited from httpd.conf.
    	# NOTE: fail2ban searches for ~/logs/*access_log and  ~/logs/*error_log
    	#       to access log files to watch and analyze!
    	ErrorLog        logs/BASE_NAME-error_log
    	CustomLog       logs/BASE_NAME-access_log combined
    
    </VirtualHost>
  3. Restart and check the web server

    […]# systemctl  restart  httpd
    […]# systemctl  status  httpd
    ● httpd.service - The Apache HTTP Server
         Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; preset: disabled)
         Active: active (running) since ...
      ...
      ...
  4. Test the configuration

    Again, enter your server’s address into your browser’s address input field. Because we already re-route everything to the secure site which uses a self-signed certificate so far, you get a warning message. Select "Advanced" and accept the 'risk' here. You’ll see the provisional test page.

  5. Final commissioning

    Delete the provisional index.html file and fill your Web content into the Document Root /srv/SITENAME/htdocs/.

Troubleshooting

The website shares the same name as the server

The phenomenon

  1. If the website uses a DocumentRoot directory different from /var/www/html, then the Servers http protocol variant will display the correct content and the https variant will use /var/www/html or the distribution provided default page instead.

  2. If you use Let’s encrypt certbot to manage certificates, it installs a valid certificate, but a https request results in an invalid certificate complain.

The cause

The httpd service always sets a "default" server. The intended use is to answer requests for which the httpd service can not identify a web server configuration. Typically, this happens when a client addresses the web page by the server’s IP address instead of its name or there is still an old DNS entry. The distribution provides a default ssl configuration as part of mod_ssl. This default gets associated with the server’s hostname or DNS record and takes precedence over the user-created configuration.

The solution

Unfortunately, the only way to fix this is to modify the distribution provided mod_ssl configuration file and remove the default server configuration. First, make a backup copy of the file and then modify it as indicated.

[…]# cp /etc/httpd/conf.d/ssl.conf  /etc/httpd/conf.d/ssl.conf.fc
[…]# vi /etc/httpd/conf.d/ssl.conf
#
# When we also provide SSL we have to listen to the
# standard HTTPS port in addition.
#
Listen 443 https

##
##  SSL Global Context
##
##  All SSL configuration in this context applies both to
##  the main server and all SSL-enabled virtual hosts.
##
...
...
##
## SSL Virtual Host Context
##

#### <VirtualHost _default_:443>     #<======= comment out this line (near line 56)

# General setup for the virtual host, inherited from global configuration
#DocumentRoot "/var/www/html"
#ServerName www.example.com:443
...
...
#   SSL Engine Switch:
#   Enable/Disable SSL for this virtual host.
####SSLEngine on                     #<======= comment out this line (near line 70)

#   List the protocol versions which clients are allowed to connect with.
#   The OpenSSL system profile is configured by default.  See
...
...
#   Point SSLCertificateFile at a PEM encoded certificate.  If
#   ...
####SSLCertificateFile /etc/pki/tls/certs/localhost.crt  #<======= comment out (near line 101)
...
...
####SSLCertificateKeyFile /etc/pki/tls/private/localhost.key   #<======= comment out (near line 110)
#   Per-Server Logging:
#   The home of a custom SSL log file. Use this when you want a
#   compact non-error SSL logfile on a virtual host basis.
CustomLog logs/ssl_request_log \
          "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"

#### </VirtualHost>     #<======= comment out this line (last line, usually 218)

[…]# systemctl  restart  httpd

When the httpd service is up again, everything should work as expected.