StackHawk
๏ƒ‰

Django CORS Guide: What It Is and How to Enable It

Matt Tanner   |   Aug 21, 2025

LinkedIn
X (Twitter)
Facebook
Reddit
Subscribe To StackHawk Posts

Have you ever built a Django API that works perfectly when tested with tools like Postman, only to find that frontend applications can’t access it from the browser? Then you’ve likely encountered CORS errors in your browser’s developer console. This article is for you if you’re running a Django backend and need to enable cross-origin requests from web applications.

We’ll walk through the process of understanding and implementing CORS in Django through a series of practical questions. By the end, you’ll understand what CORS is, whether you need it, how to configure it securely in Django, and which settings matter most for your specific use case. Letโ€™s start by looking 

What Is CORS?

CORS stands for Cross-Origin Resource Sharing, and it’s a crucial web security mechanism that controls how web pages from one domain can access resources from another domain.

To understand CORS, you first need to understand what constitutes an “origin” in web browsers. An origin consists of three components: the protocol (http/https), domain (example.com), and port (8000). This means that even if two applications share the same domain, they’re considered different origins if they use different protocols or ports.

For example, if your Django API runs at https://api.example.com:8000, that entire combination represents one origin. A frontend application running at https://app.example.com would be considered a different origin, even though they share the same base domain.

By default, browsers implement the same-origin policy, which blocks web pages from making requests to different origins. This security feature prevents malicious websites from accessing sensitive data from other sites without permission. CORS provides a standardized way to relax this restriction by using specific HTTP headers that tell browsers which cross-origin requests are safe to allow.

When a browser makes a cross-origin request, it includes an Origin header specifying where the request came from. Your Django server can then respond with CORS headers indicating whether that origin is allowed to access the requested resource.

Understanding Preflight Requests

One important aspect of CORS that often confuses developers is the concept of preflight requests. For certain types of requests, particularly those with custom headers, specific content types like application/json, or HTTP methods other than GET, HEAD, or simple POST, browsers first send a “preflight” OPTIONS request to check permissions before making the actual request.

This preflight mechanism is why you might notice extra network requests in your browser’s developer tools when making API calls. The good news is that CORS middleware like django-cors-headers handles these preflight requests automatically, but understanding this process helps explain why some requests seem slower or more complex than others.

Do You Need CORS?

Since CORS is fundamentally a security mechanism, you should only enable it when necessary and configure it as restrictively as possible while still meeting your functional requirements.

CORS is specifically needed when JavaScript running in a web browser needs to make requests to your Django application from a different origin. This doesn’t apply to all types of web interactionsโ€”simple resource embedding like images, stylesheets, and script tags are handled differently by browsers and don’t require CORS configuration.

You’ll need CORS in your Django project in these common scenarios:

Public API Development: If you’re building a Django REST API intended for consumption by third-party developers, those developers will need to make API calls directly from their web applications. Without proper CORS configuration, browsers will block these requests, making your API unusable for client-side applications.

Microservices Architecture: Modern web applications often consist of separate frontend and backend services running on different domains or ports. For example, during development, your React frontend might run on localhost:3000 while your Django API runs on localhost:8000. This separation requires CORS configuration to allow communication between the services.

Multi-domain Applications: If your organization operates multiple websites or subdomains that need to share data from a central Django backend, you’ll need CORS to enable this cross-domain data sharing while maintaining security.

On the other hand, if your Django application only serves traditional server-rendered HTML pages and doesn’t provide APIs for external consumption, you likely don’t need CORS configuration at all.

Where Should You Configure CORS?

There are several options for where to configure CORS in your infrastructure stack. Each approach has its own advantages and trade-offs, so choosing the right location is important for both maintainability and security.

You can configure CORS at the web server level (Nginx, Apache), at the CDN or proxy level (like Cloudflare), or directly within your Django application. While web server and CDN configurations can work well for static assets, they lack the flexibility and fine-grained control needed for dynamic API endpoints.

For Django applications, implementing CORS at the application level is almost always the best choice. This approach keeps your CORS configuration close to your business logic, allows for sophisticated rules based on your application’s needs, and ensures consistency across different deployment environments. You can easily version control your CORS settings alongside your code and test them in development environments.

The most important consideration is ensuring that only one layer of your infrastructure handles CORS. If multiple layers attempt to add CORS headers independently, you can end up with duplicate or conflicting headers that break functionality entirely.

Which Package Should You Use?

Unlike some web frameworks that include CORS support out of the box, Django doesn’t provide built-in CORS functionality. However, the Python ecosystem offers excellent third-party packages that integrate seamlessly with Django applications.

The most popular and well-maintained solution is django-cors-headers. This package has been the community standard for years, is actively maintained, and provides comprehensive CORS functionality that works with all modern Django versions. If you’re using Django REST Framework (which is common for API development), django-cors-headers works seamlessly without requiring any additional configuration or special DRF-specific settings.

While other CORS packages exist in the Python ecosystem, most are either unmaintained or provide fewer features than django-cors-headers. Sticking with the community standard ensures you’ll have access to documentation, community support, and regular updates.

Installing and Configuring django-cors-headers

Getting started with django-cors-headers involves a few straightforward steps. First, you’ll need to install the package and then configure it within your Django settings.

Install the package using pip:

pip install django-cors-headers

Next, you’ll need to register the package with Django by adding it to your INSTALLED_APPS setting. This tells Django to load the package and make its functionality available to your application:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # Third-party apps
    'corsheaders',
    'rest_framework',  # If using DRF
    # Your apps
    'myapp',
]

The final and most critical step is adding the CORS middleware to your MIDDLEWARE setting. The order of middleware in Django matters significantly, and for CORS to work properly, you must place the CORS middleware at the very top of your middleware stack:

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

Itโ€™s important to note that middleware order matters. The positioning of CorsMiddleware at the top is crucial because it needs to intercept and respond to CORS preflight (OPTIONS) requests before other middleware can potentially reject them. If another middleware component runs first, such as the CSRF middleware, it might block the preflight request before CORS headers can be properly added, causing your cross-origin requests to fail.

Which of Your Endpoints Need CORS Access?

One of the key advantages of django-cors-headers is that it gives you granular control over which parts of your application should accept cross-origin requests. Rather than applying CORS globally to your entire application, you can be selective about which endpoints need cross-origin access.

This selective approach is important for security reasons. Your Django application likely includes various endpoints that serve different purposes: public API endpoints that should be accessible to external applications, admin interfaces that should remain protected, user authentication pages, and internal utility endpoints. By limiting CORS to only the endpoints that actually need it, you minimize your application’s attack surface.

The most precise and recommended approach is using the CORS_URLS_REGEX setting to specify exactly which URL patterns should have CORS enabled. This setting accepts a regular expression that matches against the request URLs:

CORS_URLS_REGEX = r'^/api/.*$'

This configuration enables CORS only for URLs that start with /api/, effectively protecting your admin interface, user authentication pages, and other sensitive endpoints from cross-origin access while allowing your API endpoints to be accessed by external applications.

For more complex scenarios where you have multiple API endpoints with different URL patterns, you can create more sophisticated regular expressions:

CORS_URLS_REGEX = r'^/(api|webhooks)/.*$'

This pattern enables CORS for any URL path starting with /api/ or /webhooks/, allowing you to group multiple public-facing endpoints under a single rule while keeping everything else protected.

When defining the URLS, itโ€™s best practice to always start with the most restrictive configuration that meets your needs. It’s much easier to expand CORS access later than to restrict it after you’ve accidentally exposed endpoints that shouldn’t be accessible via cross-origin requests.

Which Origins Should Have Access?

Determining which origins can access your Django API is perhaps the most critical security decision in your CORS configuration. This decision directly impacts who can interact with your application and from where, so it requires careful consideration of your specific use case and security requirements.

The approach you take will largely depend on whether you’re building a public API meant for widespread use, an internal API for your own applications, or something in between.

Development vs. Production Strategies

Your CORS configuration will likely differ significantly between development and production environments. During development, you need flexibility and convenience to test your application across different local servers and ports. In production, you need strict security controls that only allow known, trusted origins.

For development environments, you’ll typically want to allow specific localhost origins where your frontend development servers run:

# settings/development.py
CORS_ALLOWED_ORIGINS = [
    "http://localhost:3000",  # React development server
    "http://localhost:8080",  # Vue development server
    "http://127.0.0.1:3000",  # Alternative localhost format
]

For quick development testing, you might temporarily use a more permissive setting, but be very careful not to deploy this to production:

# Only for development testing - never use in production
CORS_ALLOW_ALL_ORIGINS = True

Production environments require much more restrictive configuration. You should explicitly list each domain that needs access to your API:

# settings/production.py
CORS_ALLOWED_ORIGINS = [
    "https://yourdomain.com",
    "https://www.yourdomain.com", 
    "https://app.yourdomain.com",
]

Pattern Matching for Complex Scenarios

In some cases, you might need more flexible origin matching, particularly when dealing with multiple subdomains or dynamic environments. django-cors-headers supports regular expression patterns for these scenarios:

CORS_ALLOWED_ORIGIN_REGEXES = [
    r"^https://\w+\.yourdomain\.com$",  # Any subdomain of yourdomain.com
    r"^https://.*\.staging\.yourdomain\.com$",  # Staging environments
]

This approach is particularly useful for organizations with multiple subdomains or development environments where manually listing every possible origin would be impractical.

Public API Configuration

If you’re building a truly public API that should be accessible from any website, you have a couple of approaches. The simplest but most permissive approach is to allow all origins:

# Allow all origins - use with extreme caution
CORS_ALLOW_ALL_ORIGINS = True

# When allowing all origins, still restrict other aspects
CORS_ALLOW_CREDENTIALS = False  # Disable credentials for public APIs
CORS_ALLOWED_METHODS = ['GET', 'POST', 'OPTIONS']

A more controlled approach uses regex patterns to allow specific types of origins while maintaining some restrictions:

# More controlled approach for public APIs
CORS_ALLOWED_ORIGIN_REGEXES = [
    r"^https?://.*$",  # Allow any HTTP/HTTPS origin
]
CORS_ALLOW_CREDENTIALS = False

An important security consideration is that using CORS_ALLOW_ALL_ORIGINS = True essentially makes your API accessible to any website on the internet. This setting should only be used for read-only public APIs that don’t handle sensitive data or user authentication. For most applications, even public ones, using regex patterns provides better control and security.

What About HTTP Methods and Headers?

Beyond controlling which origins can access your API, you also need to configure which HTTP methods those origins are allowed to use and which headers they can send and receive. These settings provide additional layers of security and functionality control.

Configuring Allowed HTTP Methods

By default, django-cors-headers allows common HTTP methods, but you might want to customize this based on your API’s design and security requirements. Different endpoints might need different methods, for example, a read-only public API might only need GET requests, while a full-featured API might need the complete range of HTTP verbs.

CORS_ALLOWED_METHODS = [
    'DELETE',
    'GET', 
    'OPTIONS',  # Always required for preflight requests
    'PATCH',
    'POST',
    'PUT',
]

It’s crucial to understand that ‘OPTIONS’ must always be included in your allowed methods. This is because browsers automatically send preflight requests using the OPTIONS method for non-simple requests (those with custom headers, specific content types like application/json, or methods other than GET, HEAD, or simple POST). If you exclude ‘OPTIONS’, these preflight requests will fail, and your cross-origin requests won’t work.

For specialized use cases, you might want to be more restrictive:

# Configuration for a read-only public API
CORS_ALLOWED_METHODS = [
    'GET',
    'HEAD',
    'OPTIONS',  # Still required for preflight requests
]

Managing Request and Response Headers

Headers are another important aspect of CORS configuration. You need to specify which headers clients can send in their requests and which response headers they can access via JavaScript.

Request headers control what additional information clients can include in their API calls. This is particularly important for authentication, content type specification, and custom application headers:

# Note: Header names are case-insensitive, but lowercase is conventionally used
CORS_ALLOWED_HEADERS = [
    'accept',
    'accept-encoding',
    'authorization',      # Essential for token-based authentication
    'content-type',       # Required for JSON requests
    'origin',            # Standard CORS header
    'user-agent',        # Standard browser header
    'x-csrftoken',       # Django CSRF token
    'x-requested-with',  # Common AJAX header
    'x-api-key',         # Custom authentication header
]

Response headers determine which custom headers your API returns that JavaScript code can access. By default, browsers only expose “safe” response headers to JavaScript, so you need to explicitly expose any custom headers:

# Allow JavaScript to access these custom response headers
CORS_EXPOSE_HEADERS = [
    'x-pagination-total',    # For pagination information
    'x-rate-limit-remaining', # For rate limiting feedback
    'x-request-id',          # For debugging and tracing
]

Understanding these header configurations helps you build more sophisticated APIs while maintaining appropriate security boundaries.

Handling Authentication and Credentials

When your Django API involves user authentication, CORS configuration becomes more complex and security-critical. The way you handle authentication affects how you configure CORS, and different authentication methods have different requirements and implications.

If your Django API uses session-based authentication or any form of cookie-based authentication, you need to enable credentials in your CORS configuration. This tells browsers that it’s safe to include cookies, authorization headers, and other credentials in cross-origin requests:

CORS_ALLOW_CREDENTIALS = True

# When using credentials, you MUST specify exact origins - no wildcards
CORS_ALLOWED_ORIGINS = [
    "https://yourdomain.com",
    "https://app.yourdomain.com",
]

There’s an important security restriction here: when CORS_ALLOW_CREDENTIALS = True, you cannot use wildcard origins like CORS_ALLOW_ALL_ORIGINS = True. This is a browser-enforced security restriction designed to prevent malicious websites from making authenticated requests on behalf of users without their knowledge.

Understanding CORS vs CSRF

A common source of confusion when implementing authentication with CORS is understanding the relationship between CORS and CSRF (Cross-Site Request Forgery) protection. These are separate security mechanisms that often work together:

  • CORS controls which origins can make cross-domain requests to your API
  • CSRF protects against malicious websites making unauthorized requests using a user’s existing authentication

When using Django’s session-based authentication with cross-origin requests, you’ll typically need to configure both CORS and CSRF settings:

# CORS settings - control which origins can access your API
CORS_ALLOWED_ORIGINS = [
    "https://yourfrontend.com",
]
CORS_ALLOW_CREDENTIALS = True

# CSRF settings - validate request origins for CSRF protection
CSRF_TRUSTED_ORIGINS = [
    "https://yourfrontend.com",  # Same origins, but for CSRF validation
]

This dual configuration ensures that your frontend application can make authenticated requests while still protecting against CSRF attacks from malicious websites.

Token-Based Authentication

If you’re using token-based authentication (such as JWT tokens or Django REST Framework’s TokenAuthentication), your CORS configuration can be simpler since you typically don’t need to include cookies in requests:

CORS_ALLOW_CREDENTIALS = False  # Usually not needed for token auth

CORS_ALLOWED_HEADERS = [
    'accept',
    'authorization',  # Essential for Bearer tokens
    'content-type',
    'origin',
    'user-agent',
]

Token-based authentication often provides more flexibility with CORS because tokens are explicitly included in request headers rather than being automatically sent by browsers like cookies are.

Performance Optimization

CORS can have performance implications for your application, particularly due to preflight requests. Understanding these implications and configuring your application to minimize unnecessary overhead can improve the user experience of applications consuming your API.

Preflight Caching

One of the most effective ways to improve CORS performance is by configuring preflight caching. Preflight requests add latency to your API calls, but browsers can cache the results of these preflight requests to avoid making them repeatedly for the same endpoint and request type.

CORS_PREFLIGHT_MAX_AGE = 86400  # 24 hours in seconds

This setting tells browsers they can cache the preflight response for 24 hours, significantly reducing the number of preflight requests for frequently accessed endpoints. The optimal caching duration depends on how often your CORS configuration changes. Longer caching periods improve performance but make it take longer for CORS configuration changes to take effect for existing users, unless they clear cache or revisit in a private/incognito session.

Minimizing Preflight Triggers

Understanding what triggers preflight requests can help you design your API to minimize unnecessary preflights. Simple requests (GET, HEAD, and POST with basic content types and headers) don’t require preflights, while requests with custom headers, JSON content types, or non-standard HTTP methods always do. When possible, designing your API to use simple requests can improve performance.

Common Configuration Examples

To help you get started quickly, here are some common CORS configuration patterns for different types of Django applications:

Django + React SPA

This is one of the most common modern web application architectures, where a React (or other JavaScript framework) frontend communicates with a Django API backend:

# Development configuration
CORS_ALLOWED_ORIGINS = [
    "http://localhost:3000",  # React development server
]

# Production configuration  
CORS_ALLOWED_ORIGINS = [
    "https://myapp.com",      # Production frontend domain
    "https://www.myapp.com",  # Alternative domain
]

CORS_URLS_REGEX = r'^/api/.*$'        # Only API endpoints need CORS
CORS_ALLOW_CREDENTIALS = True          # If using session authentication
CORS_PREFLIGHT_MAX_AGE = 86400        # Cache preflight for performance

Public API

For APIs intended for public consumption by third-party developers:

CORS_ALLOW_ALL_ORIGINS = True          # Allow any origin
CORS_ALLOW_CREDENTIALS = False         # No credentials for security
CORS_ALLOWED_METHODS = [
    'GET', 
    'POST', 
    'OPTIONS'
]
CORS_EXPOSE_HEADERS = [
    'x-rate-limit-remaining',          # Help developers manage rate limits
    'x-rate-limit-reset',
]

Microservices Setup

For organizations with multiple related services that need to communicate:

CORS_ALLOWED_ORIGIN_REGEXES = [
    r"^https://.*\.mycompany\.com$",    # Any company subdomain
    r"^http://localhost:\d+$",          # Local development servers
]
CORS_URLS_REGEX = r'^/(api|graphql)/.*$'  # Multiple API types
CORS_ALLOW_CREDENTIALS = True              # For inter-service authentication

Testing Your CORS Configuration

After configuring CORS, it’s essential to verify that your configuration works correctly and provides the intended level of access control. The most straightforward way to test CORS is using your browser’s developer tools while accessing your API from a frontend application:

  1. Open your frontend application in a web browser
  2. Make a cross-origin request to your Django API
  3. Open the browser’s Network tab in developer tools
  4. Look for the CORS-related headers in both preflight OPTIONS requests and actual API responses

Verify that Access-Control-Allow-Origin and other CORS headers are present and have the expected values

Common Pitfalls and Solutions

Even with proper configuration, you might encounter CORS-related issues. Understanding common pitfalls can help you troubleshoot problems quickly.

Multiple CORS Headers

If you see duplicate CORS headers in your responses, it means multiple layers of your infrastructure are trying to handle CORS. This commonly happens when both your Django application and your web server (Nginx, Apache) or CDN are configured to add CORS headers. Ensure that only Django is handling CORS by checking your web server and CDN configurations.

Credentials with Wildcards

One of the most common CORS mistakes is trying to use CORS_ALLOW_ALL_ORIGINS = True with CORS_ALLOW_CREDENTIALS = True. This combination will always fail because browsers enforce a security restriction that requires explicit origins when credentials are involved. If you need credentials, you must specify exact origins.

Development vs Production Settings

It’s easy to accidentally deploy development CORS settings to production, which can create security vulnerabilities. Use environment-specific settings files and deployment processes that prevent overly permissive development configurations from reaching production.

Caching Issues 

Sometimes CORS problems persist even after fixing your configuration due to browser caching of preflight responses. When troubleshooting, try clearing your browser cache or testing in an incognito/private window to ensure you’re seeing the effects of your latest configuration changes.

Are There Other Considerations?

Django’s CORS middleware processes requests through the Django framework, which means it relies on your requests actually reaching Django. If you’re using custom middleware that generates responses early in the request cycle, or if you have web server configurations that bypass Django for certain requests, ensure that CORS headers are still being added appropriately.

For production deployments, establish a regular review process for your CORS configuration, especially when adding new frontend applications, changing domains, or modifying your application architecture. CORS configuration that becomes overly permissive over time can create security vulnerabilities.

Lastly, consider monitoring your application logs for CORS-related errors, which can help you identify configuration issues or potential security threats from unexpected cross-origin request attempts.

Validating Your CORS Configuration with StackHawk

Once you’ve implemented CORS headers, it’s crucial to ensure your configuration is both functional and secure. A common mistake is fixing the immediate CORS error but inadvertently creating security vulnerabilities, such as allowing credentials from any origin or being overly permissive with allowed domains.

This is where automated security testing becomes invaluable. Instead of manually testing every possible CORS scenario and hoping you haven’t introduced security gaps, you can use StackHawk to automatically validate that your CORS implementation works correctly without creating new attack vectors.

While static testing might help you spot some CORS errors, youโ€™ll need dynamic testing to ensure the runtime environment where CORS interactions occur is also free of errors. StackHawk is a DAST tool (dynamic application security testing) that will test your CORS implementation for common security issues, including:

  • Wildcard origin acceptance with credentials: Testing if your API dangerously accepts * as an origin while allowing credentials
  • Origin reflection vulnerabilities: Checking if your API blindly reflects the Origin header without proper validation
  • Missing security headers: Identifying CORS setups that lack proper CSRF protection
  • Overly permissive configurations: Finding CORS policies that are broader than necessary

Instead of waiting for our AppSec team to notify us of an issue later in development, letโ€™s use StackHawk to automatically identify this vulnerability for us.

Getting Started with StackHawk

To start testing your application for CORS vulnerabilities with StackHawk, you’ll need an account. You can sign up for a trial account. If you’re using an AI-coding assistant like Cursor or Claude Code, sign up for our $5/month single-user plan, Vibe, to find and fix vulnerabilities 100% in natural language.

If youโ€™re logging into an existing StackHawk account, from the Applications screen, youโ€™ll click Add Application.

Applications 52 findings dashboard

If youโ€™re new to StackHawk, youโ€™ll be automatically brought into the Add an App flow. On the Scanner screen, youโ€™ll see the instructions for installing the StackHawk CLI. Since we will be running our testing locally, we will use this option. Once the hawk init command is executed successfully, click the Next button.

On the next screen, you will fill out an Application Name, Environment, and URL. Once filled out, click Next.

Application Details

Since we will be testing a RESTful API, on the next page, we will choose our Application Type as โ€œAPIโ€. Depending on what you have set up as a backend API, youโ€™ll also plug these details in. For example, if you have a REST API running, set the API Type to โ€œREST / OpenAPIโ€ and point to our OpenAPI Specification file by selecting the URL Path and entering the path to your OpenAPI spec in the text box. Alternatively, as Iโ€™ve done here, we can also click โ€œSkip for nowโ€ if youโ€™re unsure. Once complete, click Next.

Application Type

Lastly, we will need to add a stackhawk.yml file to the root of our project. Once the file is added, copy the screenโ€™s contents, paste them into the file, and save it. Lastly, we can click the Finish button.

YAML Download

In our root directory, we should see the stackhawk.yml file weโ€™ve added:

stackhawk. yml

Lastly, depending on our scan policy, CORS misconfiguration detection may not be enabled. To ensure this, go back to StackHawk, navigate to the Applications page, and select your app. Once on the config page, click on the Settings tab, then the Active Scan Plugins tab, located just below it. Then, locate the CORS Header plugin in the list and select it. You can find it easily by typing “cors” into the plugin search bar.

CORS App

Lastly, click Save, located just above the Plugins list, to save the new configuration.

Run HawkScan

Next, we can go ahead with testing our application. In a terminal pointing to the root of our project, we will run HawkScan using the following command:

hawk scan

After running the command, the tests should execute in the terminal.

HawkScan terminal

Note that if you get an error similar to:

HawkScan Target Not Found Error: Unable to access https://localhost:8000. Check if the web server is listening on the specified port.

This means that your API is not running in HTTPS, and that is how HawkScan is trying to call the API. To fix this, either add HTTPS capabilities to your API or, more simply, change the host entry in your stackhawk.yml to use only “http”.

stackhawk. yml terminal

This will run tests against our Django application and the corresponding backend. Once the tests have run, we can begin to explore any findings that were discovered.

Explore The Initial Findings

Once the tests are complete, the terminal will contain some information about any vulnerabilities found. Below, we can see that StackHawk has found a few CORS vulnerabilities within my code that are present on multiple paths.

local host 4000 terminal

To explore this further, we will click on the test link at the bottom of the output. This will take us into the StackHawk platform to explore further.

Follow Link Terminal

After clicking on the link, we can now view the test results in a nicely formatted display. Next, we will click on the CORS Misconfiguration entry.

CORS Misconfig

Within this entry, we can see an Overview and Resources that can help us with fixing this vulnerability, as well as the Request and Response that the API returned on the right side of the screen. Above this, you will also see a Validate button, which will display a cURL command with the exact HTTP request used to expose the vulnerability.

Remediation of CORS Misconfig

Understanding and Fixing CORS Security Issues

When StackHawk identifies CORS vulnerabilities, you’ll see detailed findings that indicate which paths are affected and provide potential remediation techniques (as shown in the image above). Using the techniques in this guide or the CORS remediation advice provided by StackHawk for the vulnerability, you can then make the necessary adjustments to your code and configuration. Once fixed, ensure that you stop your web servers and redeploy the latest code. Next, we can ensure that the fix implemented actually resolves our CORS misconfiguration issues.

Confirm the fix!

With the latest code deployed, letโ€™s confirm the fix in StackHawk. To do this, we will click the “Rescan Findings” button in StackHawk.

Confirm Fix Rescan

Then, we will see a modal containing the โ€œhawk rescanโ€ command that includes the correct Scan ID. Youโ€™ll run this command in the same terminal where you ran the initial set of tests.

Rescan findings window

In the output, you will again see any vulnerabilities that were found during the scan. In this case, youโ€™ll see that the CORS misconfiguration vulnerabilities are no longer showing. Clicking on the link at the bottom of the terminal output, you can confirm that the CORS misconfiguration vulnerabilities have now been added to the Fixed Findings from Previous Scan, confirming that the vulnerability has been successfully fixed and has passed any vulnerability tests.

CORS App Fix Confirm

With that, weโ€™ve successfully remedied and retested our application to ensure its safety from potential CORS misconfiguration attacks.

Conclusion

When you build Django APIs that need to be accessed by frontend applications running on different domains, proper CORS configuration is essential. The django-cors-headers package provides a robust, secure, and flexible solution for managing cross-origin requests in Django applications.

The key principles to remember are:

  1. Use django-cors-headers for comprehensive, well-maintained CORS support
  2. Be specific with origins – avoid wildcards in production unless absolutely necessary
  3. Limit CORS to necessary endpoints using CORS_URLS_REGEX to minimize your attack surface
  4. Handle authentication carefully – understand the relationship between CORS and CSRF protection
  5. Test thoroughly in both development and production environments to ensure your configuration works as intended

By following these principles and carefully configuring your CORS settings based on your specific use case, you can enable the cross-origin functionality your application needs while maintaining strong security boundaries.

If youโ€™d like to learn more about configuring CORS in frameworks other than Django, check out one of our other guides:

  • FastAPI CORS โ€“ Python async framework implementation
  • Laravel CORS โ€“ PHP web framework cross-origin setup
  • Spring CORS โ€“ Java enterprise framework configuration

Want to ensure your Django CORS configuration is secure? Sign up for StackHawk today for a free 14-day trial to automatically test your CORS configuration and detect potential vulnerabilities.

FEATURED POSTS

Security Testing for the Modern Dev Team

See how StackHawk makes web application and API security part of software delivery.

Watch a Demo

Subscribe to Our Newsletter

Keep up with all of the hottest news from the Hawkโ€™s nest.

"*" indicates required fields

More Hawksome Posts