Apache and Fully Qualified Domain Names

Have you ever gotten this error when starting Apache on Linux?

httpd: Could not reliably determine the server's fully 
qualified domain name, using 127.0.0.1 for ServerName

Most advice I've seen suggests that you simply set the ServerName value in your httpd.conf file. That will certainly work. But I find the answer very unsatisfying and even a little misleading.

The quest for a more satisfying answer

I was not completely satisfied with anything I'd read online about this error so far. The goal should really be to solve the underlying issue: having a fully-qualified domain name (FQDN) set on your Linux system that Apache can actually use!

Apache's wiki has the typical advice (set ServerName and off you go.) But it also ends with the following paragraph:

https://wiki.apache.org/httpd/CouldNotDetermineServerName

The presence of this error message also indicates that Apache httpd was unable to obtain a fully-qualified hostname by doing a reverse lookup on your server's IP address. While the above instructions will get rid of the warning in any case, it is also a good idea to fix your name resolution so that this reverse mapping works.

(Emphasis mine.)

Setting ServerName in httpd.conf will solve the problem for Apache, but you will not have solved this issue for (potentially) other applications. Furthermore, it is my personal feeling that having to set both /etc/hosts and httpd.conf with the same information is a violation of the single source of truth principle.

I've done some researching and experimenting and I believe I finally have a fairly complete answer.

Let's look at some Apache source!

The source for Apache can be viewed on Github.

First, the error message we're getting ("Could not reliably determine the server's fully qualified domain name...") is coming from a function named ap_get_local_host():

util.c

ap_log_perror(APLOG_MARK, APLOG_ALERT|APLOG_STARTUP, 0, a, APLOGNO(00558)
    "%s: Could not reliably determine the server's fully qualified "
    "domain name, using %s. Set the 'ServerName' directive globally "
    "to suppress this message",
    ap_server_argv0, server_hostname);

To successfully get the FQDN for your system, it calls first apr_sockaddr_info_get() and then apr_getnameinfo() (both are part of the Apache Portable Runtime, see source for sockaddr.c here) to get the IP address and then the hostname for the address for your system.

On non-legacy POSIX systems, these functions call the C library functions getaddrinfo() and getnameinfo().

Someone please correct me if I'm wrong, but from my skimming of the Apache source and some man pages (links scattered below), it appears that the following occurs:

1. The hostname is requested

The hostname is retrieved with gethostname().

http://man7.org/linux/man-pages/man2/gethostname.2.html

2. The IP address for the hostname is requested

The IP address for the hostname is retrieved with getaddrinfo().

http://man7.org/linux/man-pages/man3/getaddrinfo.3.html

3. The FQDN for the IP address is requested

The fully-qualified domain name for the hostname's ip address is retrieved with getnameinfo().

http://man7.org/linux/man-pages/man3/getnameinfo.3.html

It should be noted that getnameinfo() uses the settings in /etc/nsswitch.conf to determine the search order to use for resolving addresses to names.

http://man7.org/linux/man-pages/man5/nsswitch.conf.5.html

By default on most systems, /etc/nsswitch.conf will contain a line like

hosts:		files dns

files specifically means /etc/hosts on most systems, so the ultimate meaning is that the contents of /etc/hosts will be checked before DNS.

What to do! (Two steps)

First, set your hostname

This seems to be system-specific

Let's use wiggles as our example hostname.

Second, edit /etc/hosts

You'll need an entry in your /etc/hosts file that resolves your hostname to an IP address AND resolves that IP address to an FQDN.

http://man7.org/linux/man-pages/man5/hosts.5.html

IP_address canonical_hostname [aliases...]

Example:

192.0.2.16       wiggles.example.com     wiggles

Note that wiggles.example.com (the FQDN) must come before wiggles (the hostname). gethostname() returns the canonical name, which is the first one.

Apache will not be impressed if it does not see a name with at least one . character. See ap_get_local_host() in util.c (linked above).

Testing at the command line!

Of course, you can keep editing things and running

apachectl restart

to see if Apache is happy yet.

But we can pretty accurately replicate what is happening internally in Apache at the command line using the hostname and getent commands!

http://man7.org/linux/man-pages/man1/hostname.1.html

http://man7.org/linux/man-pages/man1/getent.1.html

Getting the hostname

$ hostname -v   
gethostname()=`wiggles'
wiggles

(The -v option is for 'verbose'.)

Getting the IP address for the hostname

$ hostname -i
192.0.2.16 

(You can also add the -v option for more detail.)

Getting the FQDN for the IP address

$ getent hosts 192.0.2.16
192.0.2.16   wiggles.example.com  wiggles

getent hosts does specifically search in /etc/hosts. Check your /etc/nsswitch.conf to double-check the hosts lookup order (see previous information) or write your own Perl script to call the gethostbyaddr function directly!

By incrementally checking each step on the command line, I feel it's much clearer what is happening and where fixes may need to occur.