Quick answer: Exported builds lack access to the OS certificate store. Without a bundled CA certificate file, TLS cannot verify the server's identity and the handshake fails. Set Network > TLS > Certificate Bundle Path in Project Settings to a CA bundle file.

Here is how to fix Godot HTTP request SSL handshake error. Your HTTPRequest node works perfectly in the editor — API calls return data, downloads complete, leaderboards update. You export the game and every HTTPS request fails with a TLS handshake error. The connection never completes. This happens because exported builds handle SSL certificates differently from the editor, and the fix is straightforward once you understand why.

The Symptom

HTTPS requests in an exported Godot 4 build fail with result codes indicating a TLS handshake failure. The response body is empty. You may see TLS handshake error or Can't connect to host in the output log. HTTP requests without TLS still work, and the same URLs load fine in a browser. The issue only appears in exports, never in the editor.

What Causes This

1. Missing certificate bundle. The editor uses the OS root certificate store to verify TLS connections. Exported builds have no access to this store. Without a bundled CA certificate file, the TLS layer cannot verify the server’s certificate chain and refuses to connect.

2. Certificate file excluded from export. Even with the bundle path set in Project Settings, the .pem file may not be included in the exported .pck. The export resource filter must explicitly include *.pem or *.crt extensions.

3. use_threads conflicts on web/mobile. The HTTPRequest use_threads property can conflict with TLS on HTML5 exports and some Android devices, causing handshake timeouts or crashes.

The Fix

Download the Mozilla CA bundle (cacert.pem from curl.se/ca/cacert.pem), place it in your project root, and set the path in Project Settings under Network > TLS > Certificate Bundle Path. Then add *.pem to your export resource filters:

# Verify certificate bundle is accessible at runtime
extends Node

func _ready():
  var cert_path = "res://cacert.pem"
  if FileAccess.file_exists(cert_path):
    print("Certificate bundle found")
  else:
    push_error("Certificate bundle missing from export!")

Handle TLS errors gracefully and disable threads on platforms where they cause conflicts:

extends Node

@onready var http = $HTTPRequest

func _ready():
  if OS.has_feature("web"):
    http.use_threads = false
  http.request_completed.connect(_on_done)

func _on_done(result, code, headers, body):
  if result != HTTPRequest.RESULT_SUCCESS:
    push_warning("HTTP error, result: ", result)
    return
  var json = JSON.parse_string(body.get_string_from_utf8())
  print("Response: ", json)

Related Issues

If your HTTPRequest returns empty responses rather than TLS errors, the issue may be response parsing — see HTTPRequest empty response. If your exported game crashes on startup before network requests happen, check exported game crashes on startup for missing resource issues.

Download cacert.pem, add it to your project root, set the path in Project Settings. That fixes 90% of SSL issues in exports.