Fix Common nginx [emerg] Errors in a Django Config
Fix nginx [emerg] errors that stop Nginx from starting or reloading for a Django site: unknown directive, directive is not allowed here, host not found in upstream, cannot load certificate, and unexpected end of file.
Problem statement
You edited the site config, reloaded, and Nginx refused:
nginx: [emerg] unknown directive "proxy_pas" in /etc/nginx/sites-enabled/django:14
nginx: configuration file /etc/nginx/nginx.conf test failed
An [emerg] message is fatal. On reload, Nginx keeps running with the old configuration, so the site stays up but your change is not applied. On restart or after a reboot, Nginx does not start at all and every request fails. The message always includes a file and line number, and the handful of errors below cover nearly every case in a Django setup.
- Run
sudo nginx -tand read the file and line number in the message. unknown directiveis a typo or a module that is not installed.directive is not allowed heremeans it is in the wrong block (http,server, orlocation).host not found in upstreammeans a hostname inproxy_passorupstreamcannot be resolved at startup.cannot load certificatemeans a wrong path or missing file, often after Certbot changes.unexpected end of fileorunexpected "}"means unbalanced braces or a missing semicolon.
Step-by-step solution
1. Always test before reloading
sudo nginx -t && sudo systemctl reload nginx
Chaining with && means the reload only runs on a passing test. To see the full merged config with file names, use sudo nginx -T.
2. unknown directive
[emerg] unknown directive "proxy_pas" in /etc/nginx/sites-enabled/django:14
[emerg] unknown directive "brotli" in /etc/nginx/nginx.conf:30
The first is a typo. The second is a directive from a module your Nginx build does not include. Install the module package (for Brotli on Debian or Ubuntu, libnginx-mod-http-brotli-filter and -static where available) or remove the directive. Invisible characters copied from web pages, such as non-breaking spaces, also produce this error; retype the line.
3. directive is not allowed here
[emerg] "proxy_pass" directive is not allowed here in /etc/nginx/sites-enabled/django:9
[emerg] "server" directive is not allowed here in /etc/nginx/conf.d/django.conf:1
Every directive has allowed contexts. proxy_pass belongs in location; server blocks belong in http, which is where files in conf.d and sites-enabled are included. The second error typically means a site file was included from the wrong place, or a full nginx.conf (with its own http {}) was dropped into conf.d.
4. host not found in upstream
[emerg] host not found in upstream "web:8000" in /etc/nginx/conf.d/django.conf:3
Nginx resolves hostnames in proxy_pass and upstream once, at startup. If the name does not resolve then, Nginx will not start. Common cases:
- Docker Compose: the Nginx container starts before the
webservice exists on the network, or they are on different networks. Put them on the same network and adddepends_on: [web]. - A typo or a decommissioned host.
- Kubernetes Service names used outside the cluster.
To let Nginx start and resolve later, use a variable with a resolver (Docker's embedded DNS is 127.0.0.11):
resolver 127.0.0.11 valid=10s;
location / {
set $django http://web:8000;
proxy_pass $django;
}
5. cannot load certificate
[emerg] cannot load certificate "/etc/letsencrypt/live/example.com/fullchain.pem":
BIO_new_file() failed (SSL: error:80000002:system library::No such file or directory)
The path is wrong or the certificate was never issued. Check with sudo ls -l /etc/letsencrypt/live/ and sudo certbot certificates. If Certbot created example.com-0001 after a reissue, the config still points at the old directory. Point it at the directory Certbot lists, or consolidate certificates with certbot --cert-name.
6. Unbalanced braces and missing semicolons
[emerg] unexpected end of file, expecting "}" in /etc/nginx/sites-enabled/django:42
[emerg] unexpected "}" in /etc/nginx/sites-enabled/django:30
[emerg] invalid number of arguments in "proxy_set_header" directive
Each { needs a }, and every simple directive ends with ;. A missing semicolon often surfaces as "invalid number of arguments" on the next line, because Nginx reads both lines as one directive.
Verification check: sudo nginx -t prints syntax is ok and test is successful, sudo systemctl reload nginx exits cleanly, and curl -I https://example.com returns a response from Django.
Explanation
Nginx parses its whole configuration tree before applying any of it. A reload starts new worker processes with the new configuration only if parsing succeeds; otherwise the master keeps the old workers. That makes reload safe and restart dangerous, because a restart stops the running server before discovering that the new configuration cannot load. Running nginx -t in deploy scripts and configuration management catches errors before they ever reach a running server.
Edge cases / notes
- Permissions.
[emerg] open() "/etc/nginx/sites-enabled/django" failed (13: Permission denied)or failing to open log files means file permissions or SELinux contexts; see the Rocky Linux SELinux guide for the latter. - Port already in use.
bind() to 0.0.0.0:80 failed (98: Address already in use)is also[emerg]and has its own fix. - Config management. Have Ansible or your deploy script validate with
nginx -t -cagainst the new file before moving it into place. - Docker images run
nginx -g 'daemon off;'; an[emerg]makes the container exit immediately. Checkdocker logs nginx.
Next steps
- Start from a known-good file with Configure Nginx for Django in Production.
- If the config loads but the wrong site answers, see Fix Nginx Conflicting Server Name Warnings.
FAQ
Does a failed reload take my site down?
No. Nginx keeps the old configuration. A failed restart or boot does take it down.
Why does the error point at the wrong line?
A missing semicolon or brace is often reported on the following line, where parsing finally fails.
How do I see the full configuration Nginx loads?
Run sudo nginx -T. It prints every included file with its path.
Why does Nginx in Docker fail with host not found?
The upstream container name was not resolvable when Nginx started. Use a shared network, depends_on, or a resolver with a variable.