Django Debug Toolbar is a fantastic tool for understanding what’s happening under the hood of your Django application, but running it in production is a security risk. Here’s how to enable it only when you need it, without leaving it accessible to the world.

How to Use Django Debug Toolbar Without Exposing It in Production

The core problem is that Django Debug Toolbar reveals sensitive information about your application’s internals, including database queries, settings, and request/response details. If exposed in production, this information can be a goldmine for attackers.

Here’s how to tame it:

1. Restrict Access by IP Address

This is the most common and effective method. You configure Django Debug Toolbar to only show up if the request comes from a specific IP address (or a range of addresses) that you control.

  • Diagnosis: Check your settings.py for DEBUG_TOOLBAR_CONFIG.

  • Configuration: In your settings.py:

    DEBUG = True # Keep DEBUG as True for local development
    ALLOWED_HOSTS = ['localhost', '127.0.0.1', '.your_production_domain.com'] # Ensure your production domain is here
    
    INTERNAL_IPS = ['127.0.0.1'] # Or your office/VPN IP address
    
    DEBUG_TOOLBAR_CONFIG = {
        'INTERCEPT_REDIRECTS': False,
        'SHOW_TOOLBAR_CALLBACK': 'debug_toolbar.panels.Toolbar.is_valid_request',
        'RENDER_PANELS': True,
        'ENABLE_STACKTRACES': True,
        'RESULTS_CACHE_SIZE': 500,
        'ENABLE_DUPLICATE_SUPERSESSION': True,
        'SHOW_COLLAPSED': True,
        'SQL_WARNING_THRESHOLD': 100, # milliseconds
    }
    

    If you’re deploying to a server, you’ll need to find your server’s public IP or use a VPN. If your server is on AWS, for example, you might use its public IP. If you’re accessing it from your office, it would be your office’s static IP.

  • Why it works: The INTERNAL_IPS setting tells Django Debug Toolbar which IP addresses are considered "internal" and therefore allowed to see the toolbar. When a request comes in, the toolbar checks if the request’s IP address is in this list. If it is, the toolbar is rendered.

2. Use a Custom SHOW_TOOLBAR_CALLBACK

For more granular control, you can write a custom Python function that determines whether to show the toolbar. This can involve checking user groups, specific request headers, or even a temporary flag.

  • Diagnosis: You’d look for a custom callback function defined in your DEBUG_TOOLBAR_CONFIG.

  • Configuration: In settings.py:

    # ... other settings ...
    
    # Define a custom callback function in a file like your_app/utils.py
    # def my_custom_show_toolbar_callback(request):
    #     # Example: Only show if user is logged in and is a superuser
    #     return request.user.is_authenticated and request.user.is_superuser
    
    # Or, more securely, check a specific header set by a proxy/VPN
    # def my_custom_show_toolbar_callback(request):
    #     return request.META.get('HTTP_X_SHOULD_SHOW_DEBUG_TOOLBAR') == 'true'
    
    DEBUG_TOOLBAR_CONFIG = {
        'SHOW_TOOLBAR_CALLBACK': 'your_app.utils.my_custom_show_toolbar_callback',
        # ... other config ...
    }
    

    You would then need to ensure that the condition in your callback is met when you want to see the toolbar. For the header example, you’d need to configure your proxy (like Nginx or a load balancer) to add that header when you access the site.

  • Why it works: This callback function is executed for every request. If it returns True, the toolbar is shown. If it returns False, it’s suppressed. This gives you complete programmatic control over when the toolbar appears.

3. Conditional Loading Based on Environment Variables

You can use environment variables to control whether Django Debug Toolbar is enabled, ensuring it’s only active in your development or staging environments.

  • Diagnosis: Check your settings.py for logic that reads environment variables.

  • Configuration: In settings.py:

    import os
    
    DEBUG = os.environ.get('DJANGO_DEBUG', 'False') == 'True'
    
    if DEBUG:
        # Only add debug_toolbar to INSTALLED_APPS and MIDDLEWARE if DEBUG is True
        INSTALLED_APPS = [
            # ... other apps ...
            'debug_toolbar',
        ]
    
        MIDDLEWARE = [
            'debug_toolbar.middleware.DebugToolbarMiddleware',
            # ... other middleware ...
        ]
    
        # You might also conditionally set INTERNAL_IPS or other configs
        INTERNAL_IPS = ['127.0.0.1'] # Or your dev machine's IP
    else:
        # Ensure debug_toolbar is NOT loaded in production
        INSTALLED_APPS = [
            # ... other apps ...
        ]
        MIDDLEWARE = [
            # ... other middleware ...
        ]
    

    When deploying, you would set DJANGO_DEBUG=False (or omit it, as it defaults to False). Locally, you’d set DJANGO_DEBUG=True.

  • Why it works: This approach leverages the standard DEBUG setting in Django. By making DEBUG itself conditional on an environment variable, you ensure that the debug_toolbar app and middleware are only ever loaded when DEBUG is True. This is a robust way to prevent it from being accidentally enabled.

4. Use a Separate Production settings.py or Overrides

Maintain distinct settings files for development and production. This is a best practice for managing configurations.

  • Diagnosis: Look for multiple settings.py files (e.g., settings/base.py, settings/dev.py, settings/prod.py) or a mechanism for overriding settings.

  • Configuration: If you use a structure like manage.py loading myproject.settings.prod, your settings/prod.py would simply not include 'debug_toolbar' in INSTALLED_APPS or the middleware.

    Alternatively, if you use a single settings.py with environment variable overrides:

    # settings.py
    import os
    
    DEBUG = os.environ.get('DJANGO_DEBUG', 'False') == 'True'
    INTERNAL_IPS = os.environ.get('DJANGO_INTERNAL_IPS', '127.0.0.1').split(',')
    
    INSTALLED_APPS = [
        # ...
    ]
    MIDDLEWARE = [
        # ...
    ]
    
    if DEBUG:
        INSTALLED_APPS += ['debug_toolbar']
        MIDDLEWARE.insert(0, 'debug_toolbar.middleware.DebugToolbarMiddleware') # Insert at the beginning
    
        DEBUG_TOOLBAR_CONFIG = {
            'INTERNAL_IPS': INTERNAL_IPS,
            # ... other configs ...
        }
    

    Then, when running locally: DJANGO_DEBUG=True DJANGO_INTERNAL_IPS=127.0.0.1 python manage.py runserver

    And in production: DJANGO_DEBUG=False python manage.py runserver

  • Why it works: By ensuring that the debug_toolbar app and middleware are explicitly excluded from your production settings, you guarantee it cannot be loaded or run. This is the most foolproof method.

5. Local Development Server vs. Production Deployment

The simplest, yet often overlooked, solution is to never deploy your Django application with DEBUG = True. Django Debug Toolbar relies on DEBUG = True to function. If DEBUG is False in production, the toolbar won’t be active anyway.

  • Diagnosis: Check the value of DEBUG in your production deployment environment.

  • Configuration: In your production deployment script or configuration:

    # Example for a Gunicorn deployment
    gunicorn myproject.wsgi:application --bind 0.0.0.0:8000 --workers 3 --env DJANGO_DEBUG=False
    

    Or ensure your settings/prod.py has DEBUG = False.

  • Why it works: The Django Debug Toolbar’s middleware and app registration are typically conditional on DEBUG being True. When DEBUG is False, the toolbar is effectively disabled at a fundamental level, preventing it from ever being rendered or causing issues.

When you’ve successfully configured Django Debug Toolbar to only appear for your internal IPs, you’ll see the familiar toolbar appear on the right side of your browser window when accessing your site from an allowed IP. If you try from a different IP, it will be completely absent.

The next challenge you’ll likely face is optimizing your database queries, which Django Debug Toolbar will help you identify.

Want structured learning?

Take the full Django course →