• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

src/H05-Dec-2021-932711

HISTORY.rstH A D05-Dec-202110 KiB352245

LICENSEH A D07-Feb-20211.1 KiB2217

MANIFEST.inH A D30-Aug-2021255 1312

PKG-INFOH A D05-Dec-202115.2 KiB465335

README.rstH A D19-Nov-202113.6 KiB425298

pyproject.tomlH A D30-Aug-2021636 3327

setup.cfgH A D05-Dec-20211.8 KiB7869

setup.pyH A D07-Feb-202138 42

README.rst

1===================
2django-cors-headers
3===================
4
5.. image:: https://img.shields.io/github/workflow/status/adamchainz/django-cors-headers/CI/main?style=for-the-badge
6   :target: https://github.com/adamchainz/django-cors-headers/actions?workflow=CI
7
8.. image:: https://img.shields.io/badge/Coverage-100%25-success?style=for-the-badge
9  :target: https://github.com/adamchainz/django-cors-headers/actions?workflow=CI
10
11.. image:: https://img.shields.io/pypi/v/django-cors-headers.svg?style=for-the-badge
12    :target: https://pypi.org/project/django-cors-headers/
13
14.. image:: https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge
15    :target: https://github.com/psf/black
16
17.. image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=for-the-badge
18   :target: https://github.com/pre-commit/pre-commit
19   :alt: pre-commit
20
21A Django App that adds Cross-Origin Resource Sharing (CORS) headers to
22responses. This allows in-browser requests to your Django application from
23other origins.
24
25About CORS
26----------
27
28Adding CORS headers allows your resources to be accessed on other domains. It's
29important you understand the implications before adding the headers, since you
30could be unintentionally opening up your site's private data to others.
31
32Some good resources to read on the subject are:
33
34* Julia Evans' `introductory comic <https://drawings.jvns.ca/cors/>`__ and
35  `educational quiz <https://questions.wizardzines.com/cors.html>`__.
36* Jake Archibald’s `How to win at CORS <https://jakearchibald.com/2021/cors/>`__
37* The `MDN Article <https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS>`_
38* The `HTML5 Rocks Tutorial <https://www.html5rocks.com/en/tutorials/cors/>`_
39* The `Wikipedia Page <https://en.wikipedia.org/wiki/Cross-origin_resource_sharing>`_
40
41Requirements
42------------
43
44Python 3.6 to 3.10 supported.
45
46Django 2.2 to 4.0 supported.
47
48----
49
50**Are your tests slow?**
51Check out my book `Speed Up Your Django Tests <https://gumroad.com/l/suydt>`__ which covers loads of best practices so you can write faster, more accurate tests.
52
53----
54
55Setup
56-----
57
58Install from **pip**:
59
60.. code-block:: sh
61
62    python -m pip install django-cors-headers
63
64and then add it to your installed apps:
65
66.. code-block:: python
67
68    INSTALLED_APPS = [
69        ...,
70        "corsheaders",
71        ...,
72    ]
73
74Make sure you add the trailing comma or you might get a ``ModuleNotFoundError``
75(see `this blog
76post <https://adamj.eu/tech/2020/06/29/why-does-python-raise-modulenotfounderror-when-modifying-installed-apps/>`__).
77
78You will also need to add a middleware class to listen in on responses:
79
80.. code-block:: python
81
82    MIDDLEWARE = [
83        ...,
84        "corsheaders.middleware.CorsMiddleware",
85        "django.middleware.common.CommonMiddleware",
86        ...,
87    ]
88
89``CorsMiddleware`` should be placed as high as possible, especially before any
90middleware that can generate responses such as Django's ``CommonMiddleware`` or
91Whitenoise's ``WhiteNoiseMiddleware``. If it is not before, it will not be able
92to add the CORS headers to these responses.
93
94Also if you are using ``CORS_REPLACE_HTTPS_REFERER`` it should be placed before
95Django's ``CsrfViewMiddleware`` (see more below).
96
97About
98-----
99
100**django-cors-headers** was created in January 2013 by Otto Yiu. It went
101unmaintained from August 2015 and was forked in January 2016 to the package
102`django-cors-middleware <https://github.com/zestedesavoir/django-cors-middleware>`_
103by Laville Augustin at Zeste de Savoir.
104In September 2016, Adam Johnson, Ed Morley, and others gained maintenance
105responsibility for **django-cors-headers**
106(`Issue 110 <https://github.com/adamchainz/django-cors-headers/issues/110>`__)
107from Otto Yiu.
108Basically all of the changes in the forked **django-cors-middleware** were
109merged back, or re-implemented in a different way, so it should be possible to
110switch back. If there's a feature that hasn't been merged, please open an issue
111about it.
112
113**django-cors-headers** has had `40+ contributors
114<https://github.com/adamchainz/django-cors-headers/graphs/contributors>`__
115in its time; thanks to every one of them.
116
117Configuration
118-------------
119
120Configure the middleware's behaviour in your Django settings. You must set at
121least one of three following settings:
122
123* ``CORS_ALLOWED_ORIGINS``
124* ``CORS_ALLOWED_ORIGIN_REGEXES``
125* ``CORS_ALLOW_ALL_ORIGINS``
126
127``CORS_ALLOWED_ORIGINS: Sequence[str]``
128~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
129
130A list of origins that are authorized to make cross-site HTTP requests.
131Defaults to ``[]``.
132
133An Origin is defined by
134`the CORS RFC Section 3.2 <https://tools.ietf.org/html/rfc6454#section-3.2>`_
135as a URI scheme + hostname + port, or one of the special values `'null'` or
136`'file://'`.
137Default ports (HTTPS = 443, HTTP = 80) are optional here.
138
139The special value `null` is sent by the browser in
140`"privacy-sensitive contexts" <https://tools.ietf.org/html/rfc6454#section-6>`__,
141such as when the client is running from a ``file://`` domain.
142The special value `file://` is sent accidentally by some versions of Chrome on
143Android as per `this bug <https://bugs.chromium.org/p/chromium/issues/detail?id=991107>`__.
144
145Example:
146
147.. code-block:: python
148
149    CORS_ALLOWED_ORIGINS = [
150        "https://example.com",
151        "https://sub.example.com",
152        "http://localhost:8080",
153        "http://127.0.0.1:9000",
154    ]
155
156Previously this setting was called ``CORS_ORIGIN_WHITELIST``, which still works
157as an alias, with the new name taking precedence.
158
159``CORS_ALLOWED_ORIGIN_REGEXES: Sequence[str | Pattern[str]]``
160~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
161
162A list of strings representing regexes that match Origins that are authorized
163to make cross-site HTTP requests. Defaults to ``[]``. Useful when
164``CORS_ALLOWED_ORIGINS`` is impractical, such as when you have a large number
165of subdomains.
166
167Example:
168
169.. code-block:: python
170
171    CORS_ALLOWED_ORIGIN_REGEXES = [
172        r"^https://\w+\.example\.com$",
173    ]
174
175Previously this setting was called ``CORS_ORIGIN_REGEX_WHITELIST``, which still
176works as an alias, with the new name taking precedence.
177
178``CORS_ALLOW_ALL_ORIGINS: bool``
179~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
180
181If ``True``, all origins will be allowed. Other settings restricting allowed
182origins will be ignored. Defaults to ``False``.
183
184Setting this to ``True`` can be *dangerous*, as it allows any website to make
185cross-origin requests to yours. Generally you'll want to restrict the list of
186allowed origins with ``CORS_ALLOWED_ORIGINS`` or
187``CORS_ALLOWED_ORIGIN_REGEXES``.
188
189Previously this setting was called ``CORS_ORIGIN_ALLOW_ALL``, which still
190works as an alias, with the new name taking precedence.
191
192--------------
193
194The following are optional settings, for which the defaults probably suffice.
195
196``CORS_URLS_REGEX: str | Pattern[str]``
197~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
198
199A regex which restricts the URL's for which the CORS headers will be sent.
200Defaults to ``r'^.*$'``, i.e. match all URL's. Useful when you only need CORS
201on a part of your site, e.g. an API at ``/api/``.
202
203Example:
204
205.. code-block:: python
206
207    CORS_URLS_REGEX = r"^/api/.*$"
208
209``CORS_ALLOW_METHODS: Sequence[str]``
210~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
211
212A list of HTTP verbs that are allowed for the actual request. Defaults to:
213
214.. code-block:: python
215
216    CORS_ALLOW_METHODS = [
217        "DELETE",
218        "GET",
219        "OPTIONS",
220        "PATCH",
221        "POST",
222        "PUT",
223    ]
224
225The default can be imported as ``corsheaders.defaults.default_methods`` so you
226can just extend it with your custom methods. This allows you to keep up to date
227with any future changes. For example:
228
229.. code-block:: python
230
231    from corsheaders.defaults import default_methods
232
233    CORS_ALLOW_METHODS = list(default_methods) + [
234        "POKE",
235    ]
236
237``CORS_ALLOW_HEADERS: Sequence[str]``
238~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
239
240The list of non-standard HTTP headers that can be used when making the actual
241request. Defaults to:
242
243.. code-block:: python
244
245    CORS_ALLOW_HEADERS = [
246        "accept",
247        "accept-encoding",
248        "authorization",
249        "content-type",
250        "dnt",
251        "origin",
252        "user-agent",
253        "x-csrftoken",
254        "x-requested-with",
255    ]
256
257The default can be imported as ``corsheaders.defaults.default_headers`` so you
258can extend it with your custom headers. This allows you to keep up to date with
259any future changes. For example:
260
261.. code-block:: python
262
263    from corsheaders.defaults import default_headers
264
265    CORS_ALLOW_HEADERS = list(default_headers) + [
266        "my-custom-header",
267    ]
268
269``CORS_EXPOSE_HEADERS: Sequence[str]``
270~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
271
272The list of HTTP headers that are to be exposed to the browser. Defaults to
273``[]``.
274
275
276``CORS_PREFLIGHT_MAX_AGE: int``
277~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
278
279The number of seconds a client/browser can cache the preflight response. If
280this is 0 (or any falsey value), no max age header will be sent. Defaults to
281``86400`` (one day).
282
283
284**Note:** A preflight request is an extra request that is made when making a
285"not-so-simple" request (e.g. ``Content-Type`` is not
286``application/x-www-form-urlencoded``) to determine what requests the server
287actually accepts. Read more about it in the
288`CORS MDN article <https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests>`_.
289
290``CORS_ALLOW_CREDENTIALS: bool``
291~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
292
293If ``True``, cookies will be allowed to be included in cross-site HTTP
294requests. Defaults to ``False``.
295
296Note: in Django 2.1 the `SESSION_COOKIE_SAMESITE`_ setting was added, set to
297``'Lax'`` by default, which will prevent Django's session cookie being sent
298cross-domain. Change it to ``None`` to bypass this security restriction.
299
300.. _SESSION_COOKIE_SAMESITE: https://docs.djangoproject.com/en/3.0/ref/settings/#std:setting-SESSION_COOKIE_SAMESITE
301
302CSRF Integration
303----------------
304
305Most sites will need to take advantage of the `Cross-Site Request Forgery
306protection <https://docs.djangoproject.com/en/3.0/ref/csrf/>`_ that Django
307offers. CORS and CSRF are separate, and Django has no way of using your CORS
308configuration to exempt sites from the ``Referer`` checking that it does on
309secure requests. The way to do that is with its `CSRF_TRUSTED_ORIGINS setting
310<https://docs.djangoproject.com/en/3.0/ref/settings/#csrf-trusted-origins>`_.
311For example:
312
313.. code-block:: python
314
315    CORS_ALLOWED_ORIGINS = [
316        "http://read.only.com",
317        "http://change.allowed.com",
318    ]
319
320    CSRF_TRUSTED_ORIGINS = [
321        "change.allowed.com",
322    ]
323
324``CORS_REPLACE_HTTPS_REFERER: bool``
325~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
326
327``CSRF_TRUSTED_ORIGINS`` was introduced in Django 1.9, so users of earlier
328versions will need an alternate solution. If ``CORS_REPLACE_HTTPS_REFERER`` is
329``True``, ``CorsMiddleware`` will change the ``Referer`` header to something
330that will pass Django's CSRF checks whenever the CORS checks pass. Defaults to
331``False``.
332
333Note that unlike ``CSRF_TRUSTED_ORIGINS``, this setting does not allow you to
334distinguish between domains that are trusted to *read* resources by CORS and
335domains that are trusted to *change* resources by avoiding CSRF protection.
336
337With this feature enabled you should also add
338``corsheaders.middleware.CorsPostCsrfMiddleware`` after
339``django.middleware.csrf.CsrfViewMiddleware`` in your ``MIDDLEWARE_CLASSES`` to
340undo the ``Referer`` replacement:
341
342.. code-block:: python
343
344    MIDDLEWARE_CLASSES = [
345        ...,
346        "corsheaders.middleware.CorsMiddleware",
347        ...,
348        "django.middleware.csrf.CsrfViewMiddleware",
349        "corsheaders.middleware.CorsPostCsrfMiddleware",
350        ...,
351    ]
352
353Signals
354-------
355
356If you have a use case that requires more than just the above configuration,
357you can attach code to check if a given request should be allowed. For example,
358this can be used to read the list of origins you allow from a model. Attach any
359number of handlers to the ``check_request_enabled``
360`Django signal <https://docs.djangoproject.com/en/3.0/ref/signals/>`_, which
361provides the ``request`` argument (use ``**kwargs`` in your handler to protect
362against any future arguments being added). If any handler attached to the
363signal returns a truthy value, the request will be allowed.
364
365For example you might define a handler like this:
366
367.. code-block:: python
368
369    # myapp/handlers.py
370    from corsheaders.signals import check_request_enabled
371
372    from myapp.models import MySite
373
374
375    def cors_allow_mysites(sender, request, **kwargs):
376        return MySite.objects.filter(host=request.host).exists()
377
378
379    check_request_enabled.connect(cors_allow_mysites)
380
381Then connect it at app ready time using a `Django AppConfig
382<https://docs.djangoproject.com/en/3.0/ref/applications/>`_:
383
384.. code-block:: python
385
386    # myapp/__init__.py
387
388    default_app_config = "myapp.apps.MyAppConfig"
389
390.. code-block:: python
391
392    # myapp/apps.py
393
394    from django.apps import AppConfig
395
396
397    class MyAppConfig(AppConfig):
398        name = "myapp"
399
400        def ready(self):
401            # Makes sure all signal handlers are connected
402            from myapp import handlers  # noqa
403
404A common use case for the signal is to allow *all* origins to access a subset
405of URL's, whilst allowing a normal set of origins to access *all* URL's. This
406isn't possible using just the normal configuration, but it can be achieved with
407a signal handler.
408
409First set ``CORS_ALLOWED_ORIGINS`` to the list of trusted origins that are
410allowed to access every URL, and then add a handler to
411``check_request_enabled`` to allow CORS regardless of the origin for the
412unrestricted URL's. For example:
413
414.. code-block:: python
415
416    # myapp/handlers.py
417    from corsheaders.signals import check_request_enabled
418
419
420    def cors_allow_api_to_everyone(sender, request, **kwargs):
421        return request.path.startswith("/api/")
422
423
424    check_request_enabled.connect(cors_allow_api_to_everyone)
425