Django and Web server different configurations

Django is a high-level Python web framework that encourages rapid development and clean, pragmatic design. Built by experienced developers, it takes care of much of the hassle of web development, so you can focus on writing your app without needing to reinvent the wheel. It's free and open source, boasts a bustling community, and is used by many for creating a variety of web applications, from small projects to large-scale platforms.


A web server, on the other hand, is a software and hardware combination that uses HTTP (Hypertext Transfer Protocol) and other protocols to respond to user requests made over the internet. The primary function of a web server is to store, process, and deliver web pages to users. The content of the web pages can be static (pre-existing content that doesn't change) or dynamic (content that is generated or modified on-the-fly based on user interactions).

When it comes to deploying Django applications, there are several approaches available, each catering to different needs and scales of operation. These include:

  1. Shared Hosting: The most basic and cost-effective option, suitable for small applications. It involves hosting multiple websites on a single server, often with limited resources and control.

  2. Virtual Private Servers (VPS): Offers more control and dedicated resources compared to shared hosting. Users have root access to a virtual machine where they can install and configure software as needed.

  3. Dedicated Hosting: Involves renting an entire server. This option provides maximum control and resources, suitable for high-traffic websites.

  4. Cloud Hosting: Offers scalable resources and flexibility. Services like AWS (Amazon Web Services), Google Cloud Platform, and Microsoft Azure allow applications to scale resources up or down based on demand.

  5. Platform as a Service (PaaS): Providers like Heroku, Google App Engine, and AWS Elastic Beanstalk offer a higher level of abstraction, where developers can focus on application development without managing the underlying infrastructure.

Each deployment method has its advantages and considerations, including cost, scalability, control, and ease of use, making it crucial to choose one that aligns with the project's needs and goals.

When you encounter a "Forbidden - You don't have permission to access this resource" error in a Django application served by Apache, it typically relates to issues with directory permissions or incorrect Apache configuration settings. Let's go through a structured approach to troubleshoot and potentially resolve this issue.

Step 1: Check Directory Permissions

Ensure that the directory containing your Django project has the correct permissions set. Apache needs read (and execute, for directories) permissions to access the files and serve them correctly. You can adjust the permissions using the chmod and chown commands. For example, to ensure Apache (usually running under the www-data user on many Linux distributions) can access your project files, you might set:

sudo chown -R www-data:www-data /path/to/your/django/project
sudo chmod -R 755 /path/to/your/django/project

Step 2: Apache Configuration

Verify that your Apache configuration is set up correctly to serve your Django application. This involves ensuring that the mod_wsgi module is enabled and properly configured to point to your Django application's entry point. Your Apache VirtualHost configuration might look something like this:

<VirtualHost *:80>

    WSGIDaemonProcess your_project_name python-path=/path/to/your/django/project:/path/to/venv/lib/python3.x/site-packages
    WSGIProcessGroup your_project_name
    WSGIScriptAlias / /path/to/your/django/project/your_project_name/

    <Directory /path/to/your/django/project/your_project_name>
            Require all granted

    Alias /static /path/to/your/django/project/static
    <Directory /path/to/your/django/project/static>
        Require all granted

    Alias /media /path/to/your/django/project/media
    <Directory /path/to/your/django/project/media>
        Require all granted

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

Step 3: .htaccess File

If you are using a .htaccess file to configure directory-specific settings, ensure it's correctly configured and does not inadvertently block access to the resource.

Step 4: Apache and System Permissions

Beyond the directory permissions, make sure that Apache has the necessary permissions at the system level to access the directories leading up to your Django project. Every directory in the path must be accessible by the Apache user.

Step 5: SELinux Contexts

If you're on a system with SELinux enabled, ensure that the SELinux contexts for the files and directories are set correctly for Apache to access them. You might need to use commands like chcon or semanage to adjust these settings.

Step 6: Restart Apache

After making changes, don't forget to restart Apache to ensure all your changes are applied:

sudo systemctl restart apache2

This structured approach should help you identify and resolve the "Forbidden" error. If the problem persists, double-check the Apache error logs for more specific messages that can guide further troubleshooting.

Choosing the best web server for a Django application depends on various factors such as performance requirements, scalability, ease of configuration, and deployment environment. Django, being a versatile web framework, can work efficiently with several web servers. The most common choices are:

1. Gunicorn

Gunicorn (Green Unicorn) is a Python WSGI HTTP Server for UNIX. It's a pre-fork worker model ported from Ruby's Unicorn project. Gunicorn is widely regarded as a robust, efficient, and easy-to-deploy option for serving Django applications. It's lightweight, supports multiple worker processes, and is compatible with various web frameworks. Gunicorn is often used behind a reverse proxy such as Nginx.


  • Easy to set up and configure.
  • Efficient worker processes handling.
  • Wide adoption in the Django community.


  • Primarily for UNIX environments; not native to Windows.

2. uWSGI

uWSGI is another popular choice for serving Django applications. It implements the WSGI specification and the uwsgi protocol, which is a fast binary protocol for connecting with Nginx. uWSGI is highly configurable and can serve as a web server on its own or be placed behind a reverse proxy.


  • Highly configurable with a vast array of options.
  • Supports multiple languages and protocols beyond WSGI.
  • Can manage and monitor processes efficiently.


  • Complexity in configuration due to its vast array of options.

3. Daphne

Daphne is an HTTP, HTTP2, and WebSocket protocol server for ASGI and ASGI-HTTP, developed as part of the Django Channels project. It is particularly suited for Django applications that require WebSocket support for real-time features.


  • Supports asynchronous processing.
  • Handles WebSockets, HTTP2, and long-poll HTTP efficiently.
  • Good choice for real-time Django applications.


  • Might be overkill for applications that don't use Django Channels or WebSockets.

4. Nginx + Gunicorn/uWSGI

Nginx is not a WSGI server but a highly efficient HTTP and reverse proxy server. In typical Django deployments, Nginx is used as a reverse proxy to handle client connections and static files, while Gunicorn or uWSGI serves the Django application. This combination leverages Nginx's ability to handle many connections and static content efficiently, with Gunicorn/uWSGI's application serving capabilities.


  • Scalable and efficient handling of static files and client connections.
  • Security benefits from separating the public-facing server and the application server.
  • Load balancing and SSL termination can be handled by Nginx.


  • Slightly more complex setup due to the involvement of two separate systems.


For most Django deployments, Gunicorn or uWSGI with Nginx as a reverse proxy is a well-regarded choice for its balance between performance, ease of configuration, and scalability. The decision between Gunicorn and uWSGI often comes down to personal preference or specific requirements of the project. For applications requiring real-time capabilities, Daphne is a strong candidate. Ultimately, the best choice depends on the project's specific needs, the deployment environment, and the developer's familiarity with the server's configuration and management.

Setting up Gunicorn with Nginx for a Django application involves several steps that ensure efficient handling of static files, security, and scalability. Here's a comprehensive overview of how to configure Gunicorn alongside Nginx as a reverse proxy for a Django application.

Step 1: Install Gunicorn

First, install Gunicorn in your Django project's virtual environment:

pip install gunicorn

Step 2: Test Gunicorn's Ability to Serve Your Django Project

Before configuring Nginx, ensure Gunicorn can serve your Django project. Navigate to your project directory and run:

gunicorn --workers 3 your_project_name.wsgi:application

This command starts Gunicorn with three worker processes serving your Django project. Replace your_project_name with the name of your Django project. If everything is set up correctly, your project should be accessible locally at

Step 3: Install and Configure Nginx

  1. Install Nginx:

    Depending on your operating system, you can install Nginx using the appropriate package manager:

    • For Ubuntu/Debian:

      sudo apt update
      sudo apt install nginx
    • For CentOS/RHEL:

      sudo yum install nginx
    • For Fedora:

      sudo dnf install nginx
  2. Configure Nginx as a Reverse Proxy:

    Create a new Nginx server block configuration file for your project in /etc/nginx/sites-available/your_project_name and symlink it to /etc/nginx/sites-enabled/:

    sudo touch /etc/nginx/sites-available/your_project_name
    sudo ln -s /etc/nginx/sites-available/your_project_name /etc/nginx/sites-enabled

    Edit the file /etc/nginx/sites-available/your_project_name with the following configuration, replacing with your domain name and /path/to/your/django/project with the actual path to your Django project:

    server {
        listen 80;
        location = /favicon.ico { access_log off; log_not_found off; }
        location /static/ {
            root /path/to/your/django/project;
        location / {
            include proxy_params;
            proxy_pass http://unix:/path/to/your/django/project/your_project_name.sock;

    This configuration tells Nginx to serve static files directly from the /static/ directory in your Django project and forward all other requests to Gunicorn via a Unix socket.

Step 4: Run Gunicorn with a Unix Socket

Modify your Gunicorn command to use a Unix socket instead of binding to a port:

gunicorn --workers 3 --bind unix:/path/to/your/django/project/your_project_name.sock your_project_name.wsgi:application

Step 5: Adjust Permissions and Ownership

Ensure the socket file and your Django project directory are accessible by the www-data user (or the user that Nginx runs as on your system):

sudo chown -R www-data:www-data /path/to/your/django/project

Step 6: Restart and Enable Nginx

Finally, restart Nginx to apply the changes and ensure it's enabled to start on boot:

sudo systemctl restart nginx
sudo systemctl enable nginx

Step 7: Check Your Application

Now, your Django application should be accessible via your domain name. Ensure that static files are served correctly and that Gunicorn is handling the application requests as expected.

Additional Steps

  • Secure Your Application with SSL/TLS: Consider securing your application with an SSL/TLS certificate using Let's Encrypt and Certbot.
  • Use a Process Manager: To ensure Gunicorn runs continuously and starts on boot, consider using a process manager like systemd or Supervisor.

This setup provides a robust foundation for serving a Django application in a production environment, leveraging Gunicorn for application serving and Nginx for static file serving and as a reverse proxy.

Running Gunicorn directly on port 80 is generally not recommended for production environments due to security and scalability considerations. Typically, a more secure and scalable approach is to run Gunicorn on an unprivileged port (e.g., 8000) and use a reverse proxy like Nginx on port 80 to forward requests to Gunicorn. This setup allows you to leverage Nginx's efficient handling of static files and SSL termination among other benefits.

However, if you still need to run Gunicorn on port 80 directly for development or testing purposes, you must ensure it's executed with the necessary permissions since ports below 1024 are privileged and can only be bound by processes running as root.

You can run Gunicorn on port 80 by simply specifying the port number:

sudo gunicorn --workers 3 --bind your_project_name.wsgi:application

This method runs Gunicorn with root privileges, which is not recommended for security reasons.

Option 2: Use authbind (More Secure)

A more secure alternative to running Gunicorn as root is to use authbind. This allows a program that would normally require superuser privileges to access privileged network services to run as a non-privileged user.

  1. Install authbind (if not already installed):

    On Debian/Ubuntu systems, you can install authbind by running:

    sudo apt-get install authbind
  2. Allow your user to bind to port 80:

    sudo touch /etc/authbind/byport/80
    sudo chown your_user:your_user /etc/authbind/byport/80
    sudo chmod 500 /etc/authbind/byport/80

    Replace your_user with the username of the account under which you will run Gunicorn.

  3. Run Gunicorn with authbind:

    Precede your Gunicorn command with authbind --deep to run it on port 80:

    authbind --deep gunicorn --workers 3 --bind your_project_name.wsgi:application

This approach allows you to run Gunicorn on port 80 without running it as root, offering a more secure setup than running the process with elevated privileges.

Best Practice

For production environments, the best practice is to use Gunicorn in conjunction with a reverse proxy like Nginx or Apache. This configuration offers better security, load balancing, and the ability to serve static files more efficiently. If you need guidance on setting up Nginx as a reverse proxy for Gunicorn, refer to the previous instructions provided on configuring Gunicorn with Nginx.

If you're encountering a "Forbidden" error when trying to serve a Django application with Apache2 (using mod_wsgi), it usually indicates a permissions issue or a misconfiguration in your Apache2 settings. Here are steps to diagnose and resolve common causes of this error:

1. Check Directory Permissions

Apache needs read (and sometimes execute) permissions to access your Django project files and directories. Make sure the Apache user (often www-data on Ubuntu/Debian systems) has the necessary permissions.

  • Set the correct ownership and permissions for your Django project directory. For example, if your Django project is located at /var/www/mydjangoapp, you can set permissions like this:

    sudo chown -R www-data:www-data /var/www/mydjangoapp
    sudo find /var/www/mydjangoapp -type d -exec chmod 755 {} \;
    sudo find /var/www/mydjangoapp -type f -exec chmod 644 {} \;

2. Apache Configuration

Ensure your Apache configuration for the Django app is correct. You should have a .conf file for your site in /etc/apache2/sites-available/ (e.g., mydjangoapp.conf) with the proper settings to serve your Django app via mod_wsgi.

  • A basic example of such configuration might look like:

    <VirtualHost *:80>
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/mydjangoapp
        Alias /static /var/www/mydjangoapp/static
        <Directory /var/www/mydjangoapp/static>
            Require all granted
        Alias /media /var/www/mydjangoapp/media
        <Directory /var/www/mydjangoapp/media>
            Require all granted
        <Directory /var/www/mydjangoapp/mydjangoapp>
                Require all granted
        WSGIDaemonProcess mydjangoapp python-path=/var/www/mydjangoapp python-home=/var/www/mydjangoapp/venv
        WSGIProcessGroup mydjangoapp
        WSGIScriptAlias / /var/www/mydjangoapp/mydjangoapp/
  • Make sure to replace /var/www/mydjangoapp with the actual path to your Django project and adjust the python-home if your project is using a virtual environment.

3. Enable the Site and mod_wsgi

Ensure that you have enabled your site configuration and the mod_wsgi module:

  • Enable the site:

    sudo a2ensite mydjangoapp.conf
  • Enable mod_wsgi:

    sudo a2enmod wsgi

4. Check Apache Error Logs

Apache's error logs can provide specific details about what's causing the "Forbidden" error. Check the logs for any error messages related to your Django app:

  • The default location for Apache error logs on Ubuntu/Debian systems is /var/log/apache2/error.log. Tail the log file to look for relevant error messages:

    sudo tail -f /var/log/apache2/error.log

5. Restart Apache

After making changes to the permissions, configuration files, or enabling modules, remember to restart Apache to apply the changes:

sudo systemctl restart apache2

6. SELinux Contexts

If you're on a system with SELinux enabled, make sure the SELinux contexts for the files and directories are set correctly for Apache to access them. Incorrect SELinux settings can lead to "Forbidden" errors even if the traditional Unix file permissions are set correctly.

  • To temporarily set SELinux to permissive mode to see if it's causing the issue (note: this lowers security, so use it for debugging purposes only):

    sudo setenforce 0
  • If the problem is resolved, you'll need to adjust the SELinux policies accordingly instead of leaving SELinux in permissive mode.

Following these steps should help you resolve the "Forbidden" error when serving a Django application with Apache2. If the problem persists, review each step to ensure nothing was missed or misconfigured.

Configuring Apache2 to serve a Django application without using a virtual environment involves setting up mod_wsgi to point directly to your system's Python installation and the Django project's location. Here's a step-by-step guide to achieve this:

Step 1: Install Apache2 and mod_wsgi

First, ensure that Apache2 and mod_wsgi are installed on your system. mod_wsgi is an Apache module that provides a WSGI compliant interface for hosting Python-based web applications under Apache.

  • On Ubuntu/Debian systems, you can install them using:

    sudo apt update
    sudo apt install apache2 libapache2-mod-wsgi-py3

    Adjust libapache2-mod-wsgi-py3 for your Python version if necessary. This package is for Python 3.

Step 2: Configure Apache to Serve Your Django Project

  1. Create Apache Configuration File:

    You need to create a configuration file for your Django project in Apache's sites-available directory. For example, if your Django project is named mydjangoapp, you could create a configuration file named mydjangoapp.conf.

    sudo nano /etc/apache2/sites-available/mydjangoapp.conf
  2. Edit the Configuration File:

    Add the following configuration to the file, adjusting paths as necessary for your project and system:

    <VirtualHost *:80>
        ServerAdmin webmaster@localhost
        DocumentRoot /path/to/your/django/project
        Alias /static /path/to/your/django/project/static
        <Directory /path/to/your/django/project/static>
            Require all granted
        Alias /media /path/to/your/django/project/media
        <Directory /path/to/your/django/project/media>
            Require all granted
        <Directory /path/to/your/django/project/mydjangoapp>
                Require all granted
        WSGIDaemonProcess mydjangoapp python-path=/path/to/your/django/project
        WSGIProcessGroup mydjangoapp
        WSGIScriptAlias / /path/to/your/django/project/mydjangoapp/
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
    • Replace /path/to/your/django/project with the actual path to your Django project.
    • mydjangoapp should be replaced with the name of your Django project directory.
  3. Enable the Site and mod_wsgi:

    Once you've created your configuration file, you need to enable the site and the mod_wsgi module.

    sudo a2ensite mydjangoapp
    sudo a2enmod wsgi
  4. Reload Apache2:

    After making these changes, reload Apache to apply them.

    sudo systemctl reload apache2

Step 3: Adjust Permissions

Ensure that Apache has read access to your Django project's files and directories. Since you're not using a virtual environment, make sure that the system-wide Python libraries are accessible by the Apache user (typically www-data).

Step 4: Debugging

If you encounter issues, check Apache's error logs for specific error messages that can help you troubleshoot:

sudo tail -f /var/log/apache2/error.log

This setup assumes your Django project is fully compatible with the system's Python version and all necessary Django and Python dependencies are installed system-wide. It's generally recommended to use a virtual environment to avoid potential conflicts between project dependencies and system-wide Python packages. However, this setup can work well in environments where a virtual environment is not preferred or necessary.

The Require all granted directive is used in Apache's configuration files to control access to certain directories or resources served by the web server. This directive is part of Apache's access control system, introduced in version 2.4 to replace the older order/deny/allow syntax. Here's a breakdown of what it means and how it's used:


  • Require: This is the main directive used to specify access control rules. It can take various arguments to define who is allowed or denied access.

  • all: This argument specifies that the rule applies to everyone, without discrimination.

  • granted: This argument allows access. When you use Require all granted, you're allowing access to the specified resource for everyone.


The Require all granted directive is typically used within <Directory>, <Location>, <Files>, or <VirtualHost> blocks in Apache's configuration files to control access to the whole server, specific directories, locations, or files.


<Directory /var/www/html/mywebsite>
    Require all granted

This example grants access to all users for the directory /var/www/html/mywebsite and its contents.


  • Granting Access: Require all granted is used when you want to explicitly allow access to a resource. This is common for public directories or resources that should be accessible to all visitors.

  • Replacing Older Syntax: Before Apache 2.4, access control was commonly handled using the Order, Deny, and Allow directives. The equivalent of Require all granted in the older syntax would be:

    Order allow,deny
    Allow from all
  • Security Consideration: While Require all granted is useful for making resources publicly accessible, it's important to use it judiciously and ensure that sensitive directories or files are not inadvertently exposed to the public.

In summary, Require all granted is a straightforward and powerful directive used in Apache 2.4 and later to control access to web resources, making them available to all users.

Upgrading an old Django admin to a newer version involves several steps, primarily focused on upgrading your Django application as a whole. This process includes updating Django itself, dealing with deprecations, and making necessary changes to your project's codebase to ensure compatibility with the newer version. Here's a structured approach:

1. Backup Your Project

Before making any changes, back up your entire project and its database. This precaution ensures that you can revert to the original state if the upgrade process encounters significant issues.

2. Review the Django Release Notes

Before upgrading, carefully review the release notes for the version of Django you're upgrading to, as well as the notes for any intermediate versions you might be skipping. The release notes will highlight any backward-incompatible changes, new features, and deprecations. You can find the release notes on the official Django documentation website.

3. Update Django Dependency

Update the Django version in your project's requirements.txt file or whichever method you use to manage dependencies (e.g., pipenv, poetry). Specify the new Django version you want to upgrade to. For example:


4. Update Your Environment

With your dependencies file updated, apply the changes to your environment. Using pip, you would run:

pip install -r requirements.txt

5. Make Necessary Code Adjustments

Based on the deprecations and changes noted in the release notes, update your project's code. This step can involve:

  • Updating URL patterns from the old url() function to path() or re_path() functions.
  • Adjusting custom admin classes and forms to accommodate changes in the admin interface.
  • Updating model fields, views, and templates based on deprecations and new features.
  • Modifying settings for new or updated settings variables.
  • Replacing any third-party apps or middleware that are not compatible with the new Django version.

6. Run Migrations

If the Django upgrade includes changes to the models used by Django's internal apps (like auth or sessions), you'll need to create and apply migrations:

python makemigrations
python migrate

7. Test Thoroughly

After applying the changes, thoroughly test your application. Pay special attention to:

  • The admin interface, to ensure it works as expected and leverages any new features or changes.
  • User authentication and permission management, as these areas often see changes in major releases.
  • Any custom admin functionality or third-party plugins for the admin site.

8. Update Third-Party Packages

Ensure all third-party Django packages your project depends on are updated and compatible with the new Django version. This might involve updating versions in requirements.txt and making code adjustments for any changes in those packages.

9. Continuous Integration and Deployment

If you have a CI/CD pipeline, update your testing and deployment environments to reflect the new Django version and dependencies to ensure smooth deployment.

10. Review Deprecated Features

Look ahead to future releases by reviewing Django's deprecation timeline. Addressing deprecations proactively can ease future upgrades.

11. Deploy

Once your application is updated and thoroughly tested, proceed with deploying your upgraded application. Monitor the application closely following the upgrade to catch any issues early.

Upgrading Django, especially across multiple versions, can be complex and time-consuming. It's crucial to proceed carefully, test thoroughly, and consult the Django documentation and community resources for guidance.

The warning message you're encountering is related to the Cross-Origin-Opener-Policy (COOP) HTTP header and how modern browsers enforce security policies around it. This header allows you to ensure that a browser isolates your web page's process from those of other sites, which can increase security by preventing attacks such as Spectre. However, for these policies to be effectively enforced, the browser requires that the website be served over a secure context, which means using HTTPS, or being accessed via localhost during development.

Here's a breakdown of the message and steps you can take to address it:

Understanding the Warning

  • Cross-Origin-Opener-Policy Header Ignored: The browser is telling you that it won't enforce the COOP policy you've attempted to set because the policy was declared on a response from an untrustworthy origin.
  • Untrustworthy Origin: An origin is considered untrustworthy if it is not served over HTTPS (except for localhost, which is treated as a special case for development purposes).
  • Deliver the Response Using HTTPS: For the COOP header to be respected by the browser, you need to ensure your site is served over HTTPS.

How to Resolve

  1. Use HTTPS for Production: In a production environment, you should always serve your content over HTTPS to ensure security and privacy. This can be done by obtaining an SSL/TLS certificate for your domain. Free certificates can be obtained from Let's Encrypt. You'll need to configure your web server (e.g., Nginx, Apache) to use this certificate.

  2. For Local Development:

    • If you're seeing this warning during local development and you're not using localhost, consider switching to localhost for your development environment. Browsers treat localhost as a secure context.
    • Alternatively, you can set up HTTPS on your local development server. Tools like mkcert can help you create a locally-trusted certificate.
  3. Configure Web Server for HTTPS:

    • If using Nginx, you'll configure SSL settings in your server block.
    • If using Apache, you'll configure this within your VirtualHost directive.
    • Ensure you're redirecting all HTTP traffic to HTTPS to enforce secure connections.
  4. Update Any Hardcoded HTTP URLs: If your application includes hardcoded HTTP URLs (for API calls, resources, etc.), update these to HTTPS to prevent mixed content issues.

  5. Test Your Configuration: After setting up HTTPS, test your site using tools like SSL Labs' SSL Test to ensure your SSL/TLS configuration is secure.


Moving to HTTPS not only resolves the COOP header warning but also enhances the overall security and credibility of your website. Modern web development strongly emphasizes security, and serving content over HTTPS is a fundamental part of that.

The warning message you're encountering is related to the Cross-Origin-Opener-Policy (COOP) HTTP header and how modern browsers enforce security policies around it. This header allows you to ensure that a browser isolates your web page's process from those of other sites, which can increase security by preventing attacks such as Spectre. However, for these policies to be effectively enforced, the browser requires that the website be served over a secure context, which means using HTTPS, or being accessed via localhost during development.