We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
DuckDuckGo returning "access denied" errors
<!-- PLEASE FILL THESE FIELDS, IT REALLY HELPS THE MAINTAINERS OF SearXNG -->

**Version of SearXNG, commit number if you are using on master branch and stipulate if you forked SearXNG**
2023.01.06-b241015e

**How did you install SearXNG?**
searxng-docker

**What happened?**
DuckDuckGo started returning "access denied" error messages. Very similar to previous issue #1854 

**How To Reproduce**
Enable DuckDuckGo and try to search anything.

**Expected behavior**
DDG results should return and no "Access Denied" error message should be displayed.

**Screenshots & Logs**
Error message in question:
![image](https://user-images.githubusercontent.com/4787751/211107269-bc163fce-162a-4a6f-b43b-7dcd1dea3138.png)

- Exception: searx.exceptions.SearxEngineAccessDeniedException
- Parameter: HTTP error 403
- Filename: searx/search/processors/online.py:113
- Function: _send_http_request
- Code: response = req(params['url'], **request_args)

**Additional context**

It looks like it can be fixed by adding a HTTP `Referer` header to the request.
DuckDuckGo returning "access denied" errors
<!-- PLEASE FILL THESE FIELDS, IT REALLY HELPS THE MAINTAINERS OF SearXNG -->

**Version of SearXNG, commit number if you are using on master branch and stipulate if you forked SearXNG**
2023.01.06-b241015e

**How did you install SearXNG?**
searxng-docker

**What happened?**
DuckDuckGo started returning "access denied" error messages. Very similar to previous issue #1854 

**How To Reproduce**
Enable DuckDuckGo and try to search anything.

**Expected behavior**
DDG results should return and no "Access Denied" error message should be displayed.

**Screenshots & Logs**
Error message in question:
![image](https://user-images.githubusercontent.com/4787751/211107269-bc163fce-162a-4a6f-b43b-7dcd1dea3138.png)

- Exception: searx.exceptions.SearxEngineAccessDeniedException
- Parameter: HTTP error 403
- Filename: searx/search/processors/online.py:113
- Function: _send_http_request
- Code: response = req(params['url'], **request_args)

**Additional context**

It looks like it can be fixed by adding a HTTP `Referer` header to the request.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `searx/engines/duckduckgo.py`
Content:
```
1 # SPDX-License-Identifier: AGPL-3.0-or-later
2 # lint: pylint
3 """DuckDuckGo Lite
4 """
5 
6 from json import loads
7 
8 from lxml.html import fromstring
9 
10 from searx.utils import (
11     dict_subset,
12     eval_xpath,
13     eval_xpath_getindex,
14     extract_text,
15     match_language,
16 )
17 from searx.network import get
18 
19 # about
20 about = {
21     "website": 'https://lite.duckduckgo.com/lite/',
22     "wikidata_id": 'Q12805',
23     "official_api_documentation": 'https://duckduckgo.com/api',
24     "use_official_api": False,
25     "require_api_key": False,
26     "results": 'HTML',
27 }
28 
29 # engine dependent config
30 categories = ['general', 'web']
31 paging = True
32 supported_languages_url = 'https://duckduckgo.com/util/u588.js'
33 time_range_support = True
34 send_accept_language_header = True
35 
36 language_aliases = {
37     'ar-SA': 'ar-XA',
38     'es-419': 'es-XL',
39     'ja': 'jp-JP',
40     'ko': 'kr-KR',
41     'sl-SI': 'sl-SL',
42     'zh-TW': 'tzh-TW',
43     'zh-HK': 'tzh-HK',
44 }
45 
46 time_range_dict = {'day': 'd', 'week': 'w', 'month': 'm', 'year': 'y'}
47 
48 # search-url
49 url = 'https://lite.duckduckgo.com/lite/'
50 url_ping = 'https://duckduckgo.com/t/sl_l'
51 
52 # match query's language to a region code that duckduckgo will accept
53 def get_region_code(lang, lang_list=None):
54     if lang == 'all':
55         return None
56 
57     lang_code = match_language(lang, lang_list or [], language_aliases, 'wt-WT')
58     lang_parts = lang_code.split('-')
59 
60     # country code goes first
61     return lang_parts[1].lower() + '-' + lang_parts[0].lower()
62 
63 
64 def request(query, params):
65 
66     params['url'] = url
67     params['method'] = 'POST'
68 
69     params['data']['q'] = query
70 
71     # The API is not documented, so we do some reverse engineering and emulate
72     # what https://lite.duckduckgo.com/lite/ does when you press "next Page"
73     # link again and again ..
74 
75     params['headers']['Content-Type'] = 'application/x-www-form-urlencoded'
76 
77     # initial page does not have an offset
78     if params['pageno'] == 2:
79         # second page does have an offset of 30
80         offset = (params['pageno'] - 1) * 30
81         params['data']['s'] = offset
82         params['data']['dc'] = offset + 1
83 
84     elif params['pageno'] > 2:
85         # third and following pages do have an offset of 30 + n*50
86         offset = 30 + (params['pageno'] - 2) * 50
87         params['data']['s'] = offset
88         params['data']['dc'] = offset + 1
89 
90     # initial page does not have additional data in the input form
91     if params['pageno'] > 1:
92         # request the second page (and more pages) needs 'o' and 'api' arguments
93         params['data']['o'] = 'json'
94         params['data']['api'] = 'd.js'
95 
96     # initial page does not have additional data in the input form
97     if params['pageno'] > 2:
98         # request the third page (and more pages) some more arguments
99         params['data']['nextParams'] = ''
100         params['data']['v'] = ''
101         params['data']['vqd'] = ''
102 
103     region_code = get_region_code(params['language'], supported_languages)
104     if region_code:
105         params['data']['kl'] = region_code
106         params['cookies']['kl'] = region_code
107 
108     params['data']['df'] = ''
109     if params['time_range'] in time_range_dict:
110         params['data']['df'] = time_range_dict[params['time_range']]
111         params['cookies']['df'] = time_range_dict[params['time_range']]
112 
113     logger.debug("param data: %s", params['data'])
114     logger.debug("param cookies: %s", params['cookies'])
115     return params
116 
117 
118 # get response from search-request
119 def response(resp):
120 
121     headers_ping = dict_subset(resp.request.headers, ['User-Agent', 'Accept-Encoding', 'Accept', 'Cookie'])
122     get(url_ping, headers=headers_ping)
123 
124     if resp.status_code == 303:
125         return []
126 
127     results = []
128     doc = fromstring(resp.text)
129 
130     result_table = eval_xpath(doc, '//html/body/form/div[@class="filters"]/table')
131     if not len(result_table) >= 3:
132         # no more results
133         return []
134     result_table = result_table[2]
135 
136     tr_rows = eval_xpath(result_table, './/tr')
137 
138     # In the last <tr> is the form of the 'previous/next page' links
139     tr_rows = tr_rows[:-1]
140 
141     len_tr_rows = len(tr_rows)
142     offset = 0
143 
144     while len_tr_rows >= offset + 4:
145 
146         # assemble table rows we need to scrap
147         tr_title = tr_rows[offset]
148         tr_content = tr_rows[offset + 1]
149         offset += 4
150 
151         # ignore sponsored Adds <tr class="result-sponsored">
152         if tr_content.get('class') == 'result-sponsored':
153             continue
154 
155         a_tag = eval_xpath_getindex(tr_title, './/td//a[@class="result-link"]', 0, None)
156         if a_tag is None:
157             continue
158 
159         td_content = eval_xpath_getindex(tr_content, './/td[@class="result-snippet"]', 0, None)
160         if td_content is None:
161             continue
162 
163         results.append(
164             {
165                 'title': a_tag.text_content(),
166                 'content': extract_text(td_content),
167                 'url': a_tag.get('href'),
168             }
169         )
170 
171     return results
172 
173 
174 # get supported languages from their site
175 def _fetch_supported_languages(resp):
176 
177     # response is a js file with regions as an embedded object
178     response_page = resp.text
179     response_page = response_page[response_page.find('regions:{') + 8 :]
180     response_page = response_page[: response_page.find('}') + 1]
181 
182     regions_json = loads(response_page)
183     supported_languages = map((lambda x: x[3:] + '-' + x[:2].upper()), regions_json.keys())
184 
185     return list(supported_languages)
186
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/searx/engines/duckduckgo.py b/searx/engines/duckduckgo.py
--- a/searx/engines/duckduckgo.py
+++ b/searx/engines/duckduckgo.py
@@ -73,6 +73,7 @@
     # link again and again ..
 
     params['headers']['Content-Type'] = 'application/x-www-form-urlencoded'
+    params['headers']['Referer'] = 'https://lite.duckduckgo.com/'
 
     # initial page does not have an offset
     if params['pageno'] == 2:

{"golden_diff": "diff --git a/searx/engines/duckduckgo.py b/searx/engines/duckduckgo.py\n--- a/searx/engines/duckduckgo.py\n+++ b/searx/engines/duckduckgo.py\n@@ -73,6 +73,7 @@\n     # link again and again ..\n \n     params['headers']['Content-Type'] = 'application/x-www-form-urlencoded'\n+    params['headers']['Referer'] = 'https://lite.duckduckgo.com/'\n \n     # initial page does not have an offset\n     if params['pageno'] == 2:\n", "issue": "DuckDuckGo returning \"access denied\" errors\n<!-- PLEASE FILL THESE FIELDS, IT REALLY HELPS THE MAINTAINERS OF SearXNG -->\r\n\r\n**Version of SearXNG, commit number if you are using on master branch and stipulate if you forked SearXNG**\r\n2023.01.06-b241015e\r\n\r\n**How did you install SearXNG?**\r\nsearxng-docker\r\n\r\n**What happened?**\r\nDuckDuckGo started returning \"access denied\" error messages. Very similar to previous issue #1854 \r\n\r\n**How To Reproduce**\r\nEnable DuckDuckGo and try to search anything.\r\n\r\n**Expected behavior**\r\nDDG results should return and no \"Access Denied\" error message should be displayed.\r\n\r\n**Screenshots & Logs**\r\nError message in question:\r\n![image](https://user-images.githubusercontent.com/4787751/211107269-bc163fce-162a-4a6f-b43b-7dcd1dea3138.png)\r\n\r\n- Exception: searx.exceptions.SearxEngineAccessDeniedException\r\n- Parameter: HTTP error 403\r\n- Filename: searx/search/processors/online.py:113\r\n- Function: _send_http_request\r\n- Code: response = req(params['url'], **request_args)\r\n\r\n**Additional context**\r\n\r\nIt looks like it can be fixed by adding a HTTP `Referer` header to the request.\nDuckDuckGo returning \"access denied\" errors\n<!-- PLEASE FILL THESE FIELDS, IT REALLY HELPS THE MAINTAINERS OF SearXNG -->\r\n\r\n**Version of SearXNG, commit number if you are using on master branch and stipulate if you forked SearXNG**\r\n2023.01.06-b241015e\r\n\r\n**How did you install SearXNG?**\r\nsearxng-docker\r\n\r\n**What happened?**\r\nDuckDuckGo started returning \"access denied\" error messages. Very similar to previous issue #1854 \r\n\r\n**How To Reproduce**\r\nEnable DuckDuckGo and try to search anything.\r\n\r\n**Expected behavior**\r\nDDG results should return and no \"Access Denied\" error message should be displayed.\r\n\r\n**Screenshots & Logs**\r\nError message in question:\r\n![image](https://user-images.githubusercontent.com/4787751/211107269-bc163fce-162a-4a6f-b43b-7dcd1dea3138.png)\r\n\r\n- Exception: searx.exceptions.SearxEngineAccessDeniedException\r\n- Parameter: HTTP error 403\r\n- Filename: searx/search/processors/online.py:113\r\n- Function: _send_http_request\r\n- Code: response = req(params['url'], **request_args)\r\n\r\n**Additional context**\r\n\r\nIt looks like it can be fixed by adding a HTTP `Referer` header to the request.\n", "before_files": [{"content": "# SPDX-License-Identifier: AGPL-3.0-or-later\n# lint: pylint\n\"\"\"DuckDuckGo Lite\n\"\"\"\n\nfrom json import loads\n\nfrom lxml.html import fromstring\n\nfrom searx.utils import (\n    dict_subset,\n    eval_xpath,\n    eval_xpath_getindex,\n    extract_text,\n    match_language,\n)\nfrom searx.network import get\n\n# about\nabout = {\n    \"website\": 'https://lite.duckduckgo.com/lite/',\n    \"wikidata_id\": 'Q12805',\n    \"official_api_documentation\": 'https://duckduckgo.com/api',\n    \"use_official_api\": False,\n    \"require_api_key\": False,\n    \"results\": 'HTML',\n}\n\n# engine dependent config\ncategories = ['general', 'web']\npaging = True\nsupported_languages_url = 'https://duckduckgo.com/util/u588.js'\ntime_range_support = True\nsend_accept_language_header = True\n\nlanguage_aliases = {\n    'ar-SA': 'ar-XA',\n    'es-419': 'es-XL',\n    'ja': 'jp-JP',\n    'ko': 'kr-KR',\n    'sl-SI': 'sl-SL',\n    'zh-TW': 'tzh-TW',\n    'zh-HK': 'tzh-HK',\n}\n\ntime_range_dict = {'day': 'd', 'week': 'w', 'month': 'm', 'year': 'y'}\n\n# search-url\nurl = 'https://lite.duckduckgo.com/lite/'\nurl_ping = 'https://duckduckgo.com/t/sl_l'\n\n# match query's language to a region code that duckduckgo will accept\ndef get_region_code(lang, lang_list=None):\n    if lang == 'all':\n        return None\n\n    lang_code = match_language(lang, lang_list or [], language_aliases, 'wt-WT')\n    lang_parts = lang_code.split('-')\n\n    # country code goes first\n    return lang_parts[1].lower() + '-' + lang_parts[0].lower()\n\n\ndef request(query, params):\n\n    params['url'] = url\n    params['method'] = 'POST'\n\n    params['data']['q'] = query\n\n    # The API is not documented, so we do some reverse engineering and emulate\n    # what https://lite.duckduckgo.com/lite/ does when you press \"next Page\"\n    # link again and again ..\n\n    params['headers']['Content-Type'] = 'application/x-www-form-urlencoded'\n\n    # initial page does not have an offset\n    if params['pageno'] == 2:\n        # second page does have an offset of 30\n        offset = (params['pageno'] - 1) * 30\n        params['data']['s'] = offset\n        params['data']['dc'] = offset + 1\n\n    elif params['pageno'] > 2:\n        # third and following pages do have an offset of 30 + n*50\n        offset = 30 + (params['pageno'] - 2) * 50\n        params['data']['s'] = offset\n        params['data']['dc'] = offset + 1\n\n    # initial page does not have additional data in the input form\n    if params['pageno'] > 1:\n        # request the second page (and more pages) needs 'o' and 'api' arguments\n        params['data']['o'] = 'json'\n        params['data']['api'] = 'd.js'\n\n    # initial page does not have additional data in the input form\n    if params['pageno'] > 2:\n        # request the third page (and more pages) some more arguments\n        params['data']['nextParams'] = ''\n        params['data']['v'] = ''\n        params['data']['vqd'] = ''\n\n    region_code = get_region_code(params['language'], supported_languages)\n    if region_code:\n        params['data']['kl'] = region_code\n        params['cookies']['kl'] = region_code\n\n    params['data']['df'] = ''\n    if params['time_range'] in time_range_dict:\n        params['data']['df'] = time_range_dict[params['time_range']]\n        params['cookies']['df'] = time_range_dict[params['time_range']]\n\n    logger.debug(\"param data: %s\", params['data'])\n    logger.debug(\"param cookies: %s\", params['cookies'])\n    return params\n\n\n# get response from search-request\ndef response(resp):\n\n    headers_ping = dict_subset(resp.request.headers, ['User-Agent', 'Accept-Encoding', 'Accept', 'Cookie'])\n    get(url_ping, headers=headers_ping)\n\n    if resp.status_code == 303:\n        return []\n\n    results = []\n    doc = fromstring(resp.text)\n\n    result_table = eval_xpath(doc, '//html/body/form/div[@class=\"filters\"]/table')\n    if not len(result_table) >= 3:\n        # no more results\n        return []\n    result_table = result_table[2]\n\n    tr_rows = eval_xpath(result_table, './/tr')\n\n    # In the last <tr> is the form of the 'previous/next page' links\n    tr_rows = tr_rows[:-1]\n\n    len_tr_rows = len(tr_rows)\n    offset = 0\n\n    while len_tr_rows >= offset + 4:\n\n        # assemble table rows we need to scrap\n        tr_title = tr_rows[offset]\n        tr_content = tr_rows[offset + 1]\n        offset += 4\n\n        # ignore sponsored Adds <tr class=\"result-sponsored\">\n        if tr_content.get('class') == 'result-sponsored':\n            continue\n\n        a_tag = eval_xpath_getindex(tr_title, './/td//a[@class=\"result-link\"]', 0, None)\n        if a_tag is None:\n            continue\n\n        td_content = eval_xpath_getindex(tr_content, './/td[@class=\"result-snippet\"]', 0, None)\n        if td_content is None:\n            continue\n\n        results.append(\n            {\n                'title': a_tag.text_content(),\n                'content': extract_text(td_content),\n                'url': a_tag.get('href'),\n            }\n        )\n\n    return results\n\n\n# get supported languages from their site\ndef _fetch_supported_languages(resp):\n\n    # response is a js file with regions as an embedded object\n    response_page = resp.text\n    response_page = response_page[response_page.find('regions:{') + 8 :]\n    response_page = response_page[: response_page.find('}') + 1]\n\n    regions_json = loads(response_page)\n    supported_languages = map((lambda x: x[3:] + '-' + x[:2].upper()), regions_json.keys())\n\n    return list(supported_languages)\n", "path": "searx/engines/duckduckgo.py"}], "after_files": [{"content": "# SPDX-License-Identifier: AGPL-3.0-or-later\n# lint: pylint\n\"\"\"DuckDuckGo Lite\n\"\"\"\n\nfrom json import loads\n\nfrom lxml.html import fromstring\n\nfrom searx.utils import (\n    dict_subset,\n    eval_xpath,\n    eval_xpath_getindex,\n    extract_text,\n    match_language,\n)\nfrom searx.network import get\n\n# about\nabout = {\n    \"website\": 'https://lite.duckduckgo.com/lite/',\n    \"wikidata_id\": 'Q12805',\n    \"official_api_documentation\": 'https://duckduckgo.com/api',\n    \"use_official_api\": False,\n    \"require_api_key\": False,\n    \"results\": 'HTML',\n}\n\n# engine dependent config\ncategories = ['general', 'web']\npaging = True\nsupported_languages_url = 'https://duckduckgo.com/util/u588.js'\ntime_range_support = True\nsend_accept_language_header = True\n\nlanguage_aliases = {\n    'ar-SA': 'ar-XA',\n    'es-419': 'es-XL',\n    'ja': 'jp-JP',\n    'ko': 'kr-KR',\n    'sl-SI': 'sl-SL',\n    'zh-TW': 'tzh-TW',\n    'zh-HK': 'tzh-HK',\n}\n\ntime_range_dict = {'day': 'd', 'week': 'w', 'month': 'm', 'year': 'y'}\n\n# search-url\nurl = 'https://lite.duckduckgo.com/lite/'\nurl_ping = 'https://duckduckgo.com/t/sl_l'\n\n# match query's language to a region code that duckduckgo will accept\ndef get_region_code(lang, lang_list=None):\n    if lang == 'all':\n        return None\n\n    lang_code = match_language(lang, lang_list or [], language_aliases, 'wt-WT')\n    lang_parts = lang_code.split('-')\n\n    # country code goes first\n    return lang_parts[1].lower() + '-' + lang_parts[0].lower()\n\n\ndef request(query, params):\n\n    params['url'] = url\n    params['method'] = 'POST'\n\n    params['data']['q'] = query\n\n    # The API is not documented, so we do some reverse engineering and emulate\n    # what https://lite.duckduckgo.com/lite/ does when you press \"next Page\"\n    # link again and again ..\n\n    params['headers']['Content-Type'] = 'application/x-www-form-urlencoded'\n    params['headers']['Referer'] = 'https://lite.duckduckgo.com/'\n\n    # initial page does not have an offset\n    if params['pageno'] == 2:\n        # second page does have an offset of 30\n        offset = (params['pageno'] - 1) * 30\n        params['data']['s'] = offset\n        params['data']['dc'] = offset + 1\n\n    elif params['pageno'] > 2:\n        # third and following pages do have an offset of 30 + n*50\n        offset = 30 + (params['pageno'] - 2) * 50\n        params['data']['s'] = offset\n        params['data']['dc'] = offset + 1\n\n    # initial page does not have additional data in the input form\n    if params['pageno'] > 1:\n        # request the second page (and more pages) needs 'o' and 'api' arguments\n        params['data']['o'] = 'json'\n        params['data']['api'] = 'd.js'\n\n    # initial page does not have additional data in the input form\n    if params['pageno'] > 2:\n        # request the third page (and more pages) some more arguments\n        params['data']['nextParams'] = ''\n        params['data']['v'] = ''\n        params['data']['vqd'] = ''\n\n    region_code = get_region_code(params['language'], supported_languages)\n    if region_code:\n        params['data']['kl'] = region_code\n        params['cookies']['kl'] = region_code\n\n    params['data']['df'] = ''\n    if params['time_range'] in time_range_dict:\n        params['data']['df'] = time_range_dict[params['time_range']]\n        params['cookies']['df'] = time_range_dict[params['time_range']]\n\n    logger.debug(\"param data: %s\", params['data'])\n    logger.debug(\"param cookies: %s\", params['cookies'])\n    return params\n\n\n# get response from search-request\ndef response(resp):\n\n    headers_ping = dict_subset(resp.request.headers, ['User-Agent', 'Accept-Encoding', 'Accept', 'Cookie'])\n    get(url_ping, headers=headers_ping)\n\n    if resp.status_code == 303:\n        return []\n\n    results = []\n    doc = fromstring(resp.text)\n\n    result_table = eval_xpath(doc, '//html/body/form/div[@class=\"filters\"]/table')\n    if not len(result_table) >= 3:\n        # no more results\n        return []\n    result_table = result_table[2]\n\n    tr_rows = eval_xpath(result_table, './/tr')\n\n    # In the last <tr> is the form of the 'previous/next page' links\n    tr_rows = tr_rows[:-1]\n\n    len_tr_rows = len(tr_rows)\n    offset = 0\n\n    while len_tr_rows >= offset + 4:\n\n        # assemble table rows we need to scrap\n        tr_title = tr_rows[offset]\n        tr_content = tr_rows[offset + 1]\n        offset += 4\n\n        # ignore sponsored Adds <tr class=\"result-sponsored\">\n        if tr_content.get('class') == 'result-sponsored':\n            continue\n\n        a_tag = eval_xpath_getindex(tr_title, './/td//a[@class=\"result-link\"]', 0, None)\n        if a_tag is None:\n            continue\n\n        td_content = eval_xpath_getindex(tr_content, './/td[@class=\"result-snippet\"]', 0, None)\n        if td_content is None:\n            continue\n\n        results.append(\n            {\n                'title': a_tag.text_content(),\n                'content': extract_text(td_content),\n                'url': a_tag.get('href'),\n            }\n        )\n\n    return results\n\n\n# get supported languages from their site\ndef _fetch_supported_languages(resp):\n\n    # response is a js file with regions as an embedded object\n    response_page = resp.text\n    response_page = response_page[response_page.find('regions:{') + 8 :]\n    response_page = response_page[: response_page.find('}') + 1]\n\n    regions_json = loads(response_page)\n    supported_languages = map((lambda x: x[3:] + '-' + x[:2].upper()), regions_json.keys())\n\n    return list(supported_languages)\n", "path": "searx/engines/duckduckgo.py"}]}