Setting up a basic web server

Peter Boy (pboy) Version F37,F38 Last review: 2023-05-xx

You are in the Fedora Server documentation staging area!

These documents are not approved yet and may be incomplete and/or incorrect. Take everything here with a grain of salt! You would probably prefer to study the published documentation.

Status of this document: Work in progress.

The recommended and fully suppported 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 more sophisticated configurations.

This article describes the installation and a basic configuration of the Fedora Web server, httpd. It is basically an Apache web server. It 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. Otherwise, it is limited to the simple delivery of static web pages. Other articles cover more complex deployment options such as front end for an application server, proxy for containers and the like.

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 located under /var/www, the html-files in the subdirectory html, special configurations in further subdirectories to be created if necessary. But this directory is practically meaningless for the current versions.

In earlier years, a web server served only one site (i..e one Domain), still virulent in the term "main“ site or „host". Later, "virtual" sites (or "hosts") were added, distinguished either by name (virtual named hosts) or by IP.

All these distinctions have now been abandoned. It is now best practice to configure everything as a virtual hosts, even if a server serves only one domain. The old "main" is still present as default configuration of the most important parameters and you could simply use that "main" site and put files into the /var/www/html subdirectory. But best practice today is to create a custom configuration for each domain using <VirtualHost> directive, defining only deviations from or additions to "main".

This development has consequences for the organization of data storage. If in former times the data of the server was stored according to FHS in /var/lib and/or /var/www, today one has many servers in the sense of Domains, which are served by a single Application. According to the FHS, the /srv directory is the appropriate place for storing this data.

Therefore, we use directories like /srv/<DOMAINNAME> here to store all data relevant to a domain, e.g. /srv/<DOMAINMANE>/htdocs for static HTML pages or /srv/<DOMAINNAME>/WEBAPP for the domain-specific part of a web application lie Redmine.

Storage preparation

By default, httpd stores

Installation

  1. Install the Apache httpd web server. In most cases you need nowerdays reularily the modules to manage ssl connections and the domain monitoring module as well. Everything is using https today.

    […]$ 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  httpdt
    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 directory

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

    […]$ sudo -i
    […]# mkdir -p  /srv/SITENAME/htdocs
    […]# /usr/sbin/semanage fcontext -a -t httpd_sys_content_t  -s system_u  "/srv/leto-01/htdocs(/.*)?"
    […]# /sbin/restorecon -R -vF /srv/leto-01/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

    […]# vi /srv/leto-01.htdocs/index.html
    <h1><center>It works!</center></h1>
  2. Configure a Website for use with an explicite letsencrypt certificate

    A web site configuration file is stored in /etc/httpd/conf.d. As a naming convention, we prefix these files with vhost- to clearly distinguish the web site configurations from other configurations.

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

    […]$ sudo vi /etc/httpd/conf.d/vhost-SITENAME.conf
    <VirtualHost *:443>
            # leto-01 main host web access
    
            ServerName      leto-01.resdigita.de
            ServerAlias     www.SITENAME.DOMAIN_NAME
            ServerAdmin     root@localhost
    
            SSLEngine on
            # DEFAULT mod_ssl provided, comment OUT when you create custom certificate!
            SSLCertificateFile /etc/pki/tls/certs/localhost.crt
            SSLCertificateKeyFile /etc/pki/tls/private/localhost.key
    
            # LetsEncrypt certificates
            #SSLCertificateFile      /etc/letsencrypt/live/FQDN_NAME/cert.pem
            #SSLCertificateKeyFile   /etc/letsencrypt/live/FQDN_NAME/privkey.pem
            #SSLCertificateChainFile /etc/letsencrypt/live/FQDN_NAME/chain.pem
    
            DirectoryIndex  index.html
            DocumentRoot    /srv/SITENAME/htdocs
            # Specific to default 2.4 configuration:
            # Enable access to server-specific base file location
            <Directory "/srv/SITENAME">
                    AllowOverride None
                    # Allow open access:
                    Require all granted
            </Directory>
            # Further relax access to the default document root
            <Directory "/srv/SITENAME/htdocs">
                    Options Indexes FollowSymLinks
                    AllowOverride None
                    Require all granted
            </Directory>
    
            # Use separate log files for the virtual host; note that LogLevel
            # is not inherited from httpd.conf.
            ErrorLog        logs/SITENAME-ssl_error_log
            CustomLog       logs/SITENAME-ssl_access_log combined
            LogLevel warn
    
    </VirtualHost>
    
    <VirtualHost *:80>
            # leto-01
    
            ServerName      SITENAME.DOMAIN
            ServerAlias     www.SITENAME.DOMAIN
            ServerAdmin     root@localhost
    
            # ##########################################################################
            # NOTE: We re-route everything to the secure site!
            #       Only exception is access to .well-known to manage letsencrypt
            #       certificate renewal.
            RewriteEngine   On
            # Don't redirect letsencrypt
            RewriteCond     %{REQUEST_URI}  !^/.well-known(.*)$ [NC]
            RewriteRule     ^(.*)$          https://SITENAME.DOMAIN  [R=301,L]
    
            # ######################################################################
            # NOTE: rule above re-routes everything (besides letsencrypt) to https!
            #       Everything below is a pre-configuration in case of emergency /
            #       issues with certificates.
    
            DirectoryIndex  index.html
            DocumentRoot    /srv/SITENAME/htdocs
            # Specific to default 2.4 configuration:
            # Enable access to server-specific base file location
            <Directory "/srv/SITENAME">
                    AllowOverride None
                    # Allow open access:
                    Require all granted
            </Directory>
            # Further relax access to the default document root
            <Directory "/srv/SITENAME/htdocs">
                    Options Indexes FollowSymLinks
                    AllowOverride None
                    Require all granted
            </Directory>
    
            # Use separate log files for the virtual host; note that LogLevel
            # is not inherited from httpd.conf.
            ErrorLog        logs/SITENAME-error_log
            CustomLog       logs/SITENAME-access_log combined
    
    </VirtualHost>

    You may have a look on a more sophisticated template file, adjust it to your needs and use it for all your various domains.

    Finally, 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 ...
      ...
      ...
  3. 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 test page again.

Troubleshooting

The website shares the same name as the server

The phenomenon

  1. If the website uses a DocumentRoot directory other than /var/www/html, then the http version will display the correct content and the https variant will use /var/www/html instead.

  2. Letsencrypt 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. 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 configuration file. 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.bak
[…]# 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 (mostly 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 (mostly line 70)      <=======

#   List the protocol versions which clients are allowed to connect with.
#   The OpenSSL system profile is configured by default.  See
...
...
#   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, mostly 218)    <=======

[…]# systemctl  restart  httpd

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