I've set up basic auth on some pages on my site. Server logs are showing that the authentication is working, however, once I'm logged in all the protected pages return 404 errors - and not my actual 404 page either, but the generic Nginx 404 page.

It's a Django app running on Heroku. On my development server which has basic auth disabled, the pages load correctly, so there is nothing wrong with my routes.

Here's my nginx.conf.erb:

daemon off;
#Heroku dynos have at least 4 cores.
worker_processes <%= ENV['NGINX_WORKERS'] || 4 %>;

events {
    use epoll;
    accept_mutex on;
    worker_connections 1024;

http {

    gzip on;
    gzip_comp_level 2;
    gzip_min_length 512;

    server_tokens off;

    include mime.types;
    default_type application/octet-stream;
    sendfile on;

    client_body_timeout 5;

    upstream app_server {
        server unix:/tmp/nginx.socket fail_timeout=0;

    server {
        listen <%= ENV["PORT"] %>;
        server_name _;
        keepalive_timeout 5;

        root /app;

        location / {

            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
            proxy_redirect off;
            proxy_pass http://app_server;


        location /protected {

            try_files $uri $uri/ =404;

            auth_basic "Restricted";
            auth_basic_user_file /app/config/.htpasswd;

Here's the log output, when I try to access the protected page after logging in:

2018-06-19T06:58:10.612050+00:00 heroku[router]: at=info method=GET path="/protected/" host=mysite.com request_id=fe3aedbc-f31b-4012-9084-6ec7c194583d fwd="," dyno=web.1 connect=1ms service=2ms status=404 bytes=707 protocol=https

2018-06-19T06:58:10.611886+00:00 app[web.1]: measure#nginx.service=0.001 request="GET /protected/ HTTP/1.1" status_code=404 request_id=fe3aedbc-f31b-4012-9084-6ec7c194583d remote_addr="" forwarded_for="," forwarded_proto="https" via="1.1 vegur" body_bytes_sent=564 referer="-" user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.25 Safari/537.36"
  • Where is your files stored? In /app/protected? Commented Jun 19, 2018 at 13:04
  • It's a Django template, currently, I have it set to just output the word "hello". However, where this actually came up is in trying to use Django Anymail to get a webhook from Mailgun. Anymail uses basic auth to authenticate webhooks, but all pages protected with basic auth are returning 404, so I'm trying to make the simplest possible page work and build from there.
    – Lewy Blue
    Commented Jun 20, 2018 at 3:27
  • Note that django-anymail implements its own basic auth checks for webhooks, so unless there are other pages you want to protect, there shouldn't be any need to also use auth_basic in nginx.
    – medmunds
    Commented Aug 2, 2018 at 21:38

2 Answers 2


You probably need to repeat proxy_pass inside the second location block:

location /protected {
    try_files $uri $uri/ =404;
    proxy_pass http://app_server; # add this line

    auth_basic "Restricted";
    auth_basic_user_file /app/config/.htpasswd;

I've had the same problem with a configuration similar to yours. To investigate the issue, I've opened the nginx logs (tail -f /var/log/nginx/error.log). There, I've seen a couple of open() "/usr/share/nginx/html/my/endpoint/path" failed (2: No such file or directory) – this suggested that nginx doesn't perform reverse proxy without explicit proxy_pass in each location block.

  • This seems like a step in the right direction, however now Django is returning an error saying Invalid HTTP_HOST header: 'app_server'. The domain name provided is not valid according to RFC 1034/1035.
    – Lewy Blue
    Commented Aug 2, 2018 at 16:18
  • You'll likely need to repeat all the proxy_* directives in the second location block—I don't think they're inherited. In particular, proxy_set_header Host $http_host will let Django see the correct (external) host from the http request.
    – medmunds
    Commented Aug 2, 2018 at 21:42

In my case, the 404 happened because my root directory reverted back to the default. For Debian, I think the default root is /usr/share/nginx/html

location ... {
  # Forgot to set the root...
  root /home/www-data/example.com/restricted;
  auth_basic "Restricted Area";
  auth_basic_user_file thepass;

I probably could have nested this location block inside the main location block (where I have the root set) but decided to keep it separate like this...

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .