Simplificado demais: você precisa de algo que execute o Python, mas o Python não é o melhor para lidar com todos os tipos de solicitações.
[aviso: sou desenvolvedor do Gunicorn]
Menos simplificado: independentemente do servidor de aplicativos que você usa (Gunicorn, mod_wsgi, mod_uwsgi, cherrypy), qualquer tipo de implantação não trivial terá algo a montante que manipulará as solicitações que seu aplicativo Django não deve atender. Exemplos triviais de tais solicitações estão servindo ativos estáticos (images / css / js).
Isso resulta em duas primeiras camadas da clássica "arquitetura de três camadas". Ou seja, o servidor da web (Nginx no seu caso) atenderá a muitos pedidos de imagens e recursos estáticos. Solicitações que precisam ser geradas dinamicamente serão repassadas ao servidor de aplicativos (Gunicorn no seu exemplo). (Como um aparte, o terceiro dos três níveis é o banco de dados)
Historicamente, cada uma dessas camadas seria hospedada em máquinas separadas (e provavelmente haveria várias máquinas nas duas primeiras camadas, ou seja: 5 servidores da Web enviam solicitações para dois servidores de aplicativos que, por sua vez, consultam um único banco de dados).
Na era moderna, agora temos aplicações de todas as formas e tamanhos. Nem todo projeto de fim de semana ou site de empresa de pequeno porte realmente precisa da potência de várias máquinas e funciona muito bem em uma única caixa. Isso gerou novas entradas no conjunto de soluções de hospedagem. Algumas soluções casarão o servidor de aplicativos com o servidor Web (Apache httpd + mod_wsgi, Nginx + mod_uwsgi, etc). E não é incomum hospedar o banco de dados na mesma máquina que uma dessas combinações de servidores de aplicativos / web.
Agora, no caso de Gunicorn, tomamos uma decisão específica (copiando do Ruby Unicorn) de manter as coisas separadas do Nginx enquanto confiamos no comportamento de proxy do Nginx. Especificamente, se pudermos supor que o Gunicorn nunca lerá conexões diretamente da Internet, não precisaremos nos preocupar com clientes que são lentos. Isso significa que o modelo de processamento do Gunicorn é embaraçosamente simples.
A separação também permite que o Gunicorn seja escrito em Python puro, o que minimiza o custo do desenvolvimento sem afetar significativamente o desempenho. Ele também permite que os usuários usem outros proxies (supondo que sejam armazenados em buffer corretamente).
Quanto à sua segunda pergunta sobre o que realmente lida com a solicitação HTTP, a resposta simples é Gunicorn. A resposta completa é que Nginx e Gunicorn tratam da solicitação. Basicamente, o Nginx receberá a solicitação e, se for uma solicitação dinâmica (geralmente baseada em padrões de URL), enviará essa solicitação ao Gunicorn, que a processará e, em seguida, retornará uma resposta ao Nginx, que encaminhará a resposta ao original. cliente.
Então, fechando, sim. Você precisa do Nginx e do Gunicorn (ou algo semelhante) para uma implementação apropriada do Django. Se você está procurando hospedar o Django especificamente com o Nginx, eu investigaria o Gunicorn, mod_uwsgi e talvez o CherryPy como candidatos ao lado do Django.