DB-유

Installing and Configuring nginx on CentOS 7

· linux, nginx, centos

nginx has been my default web server for a few years. The config is readable and it copes with a lot of connections without tuning. Coming from Ubuntu, though, CentOS 7 always catches me out: no nginx in the base repos, firewalld closed, SELinux blocking files. Here's how I set it up, on a box where you have sudo (a DNS name is optional, an IP works for testing).

The repository

EPEL carries nginx too, but it lags behind. I use nginx.org's stable branch (1.12 at the moment). Create /etc/yum.repos.d/nginx.repo:

[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=0
enabled=1

Then install:

sudo yum -y install nginx
nginx -v

You should see nginx version: nginx/1.12.0 or later.

Start it

sudo systemctl enable nginx
sudo systemctl start nginx
sudo systemctl status nginx

The main config is /etc/nginx/nginx.conf, which includes every *.conf in /etc/nginx/conf.d/. The default site is conf.d/default.conf, root /usr/share/nginx/html.

Firewall

firewalld blocks everything except SSH by default. Open HTTP and HTTPS permanently and reload:

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-services

At this point curl -I http://your-server-ip/ from another machine should return HTTP/1.1 200 OK and Server: nginx/1.12.0.

One server block per site

I keep one file per site in conf.d and one document root per site under /var/www. It's boring and it scales. Make the directory and a test page:

sudo mkdir -p /var/www/example.com/html
echo '<h1>example.com works</h1>' | sudo tee /var/www/example.com/html/index.html
sudo chown -R nginx:nginx /var/www/example.com

Then create /etc/nginx/conf.d/example.com.conf:

server {
    listen 80;
    server_name example.com www.example.com;

    root /var/www/example.com/html;
    index index.html;

    access_log /var/log/nginx/example.com.access.log;
    error_log  /var/log/nginx/example.com.error.log;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
        expires 30d;
        add_header Cache-Control "public";
    }
}

Rename the default server block so it does not conflict (mv default.conf default.conf.disabled) or leave it as the catch-all for requests with an unknown Host header.

SELinux

Ubuntu people forget this one. With SELinux enforcing, nginx can only read files labelled httpd_sys_content_t. /usr/share/nginx/html and /var/www get it by default, but a directory somewhere else, or files moved from a home directory with mv (which keeps the old label), gives you 403s. Make the label permanent and apply it:

sudo semanage fcontext -a -t httpd_sys_content_t "/var/www(/.*)?"
sudo restorecon -Rv /var/www

If nginx also needs to proxy to a backend, allow outbound connections:

sudo setsebool -P httpd_can_network_connect 1

Test, then reload

sudo nginx -t
sudo systemctl reload nginx

nginx -t prints syntax is ok and test is successful. I always run it before a reload. A bad config on reload fails harmlessly, but on restart it leaves you with no web server.

Does it serve?

curl -H "Host: example.com" http://127.0.0.1/
curl -I http://example.com/
sudo tail -f /var/log/nginx/example.com.access.log

The first should return your test HTML regardless of DNS. Watch the access log while you load the site in a browser to confirm requests hit the right block.

Gotchas

  • 403 Forbidden with (13: Permission denied) in the error log is almost always SELinux labelling. Check ls -Z /var/www/example.com/html for httpd_sys_content_t; ausearch -m avc -ts recent shows the denials.
  • nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use) means Apache (httpd) is running. systemctl disable --now httpd.
  • nginx: [warn] conflicting server name "example.com" on 0.0.0.0:80, ignored means two blocks claim the same name, usually a leftover default.conf.
  • Works with curl on the server but times out remotely: firewalld. Check firewall-cmd --list-all and that you ran --reload after the --permanent changes. I've done that one more than once.